diff --git a/README.md b/README.md index 7a3ead0..8cd0e79 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/cmd/age/age.go b/cmd/age/age.go index 165f29c..8af316f 100644 --- a/cmd/age/age.go +++ b/cmd/age/age.go @@ -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) } diff --git a/cmd/age/parse.go b/cmd/age/parse.go index 4949a59..22b8acf 100644 --- a/cmd/age/parse.go +++ b/cmd/age/parse.go @@ -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 }