From f3fdc33c9efcbe5c83e99751eac7097df95c4973 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Tue, 9 Mar 2021 20:42:20 -0500 Subject: [PATCH] cmd/age-keygen: add -y mode to convert identity file to recipients Copied -y from ssh-keygen. Copied the INPUT as only optional argument from cmd/age. Fixes #122 Closes #146 --- cmd/age-keygen/keygen.go | 57 +++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/cmd/age-keygen/keygen.go b/cmd/age-keygen/keygen.go index cf6972a4..c744b484 100644 --- a/cmd/age-keygen/keygen.go +++ b/cmd/age-keygen/keygen.go @@ -9,6 +9,7 @@ package main import ( "flag" "fmt" + "io" "log" "os" "runtime/debug" @@ -20,9 +21,11 @@ import ( const usage = `Usage: age-keygen [-o OUTPUT] + age-keygen -y [-o OUTPUT] [INPUT] Options: - -o, --output OUTPUT Write the key to the file at path OUTPUT. + -o, --output OUTPUT Write the result to the file at path OUTPUT. + -y Convert an identity file to a recipients file. age-keygen generates a new standard X25519 key pair, and outputs it to standard output or to the OUTPUT file. @@ -30,6 +33,10 @@ standard output or to the OUTPUT file. If an OUTPUT file is specified, the public key is printed to standard error. If OUTPUT already exists, it is not overwritten. +In -y mode, age-keygen reads an identity file from INPUT or from standard +input and writes the corresponding recipient(s) to OUTPUT or to standard +output, one per line, with no comments. + Examples: $ age-keygen @@ -38,7 +45,10 @@ Examples: AGE-SECRET-KEY-1N9JEPW6DWJ0ZQUDX63F5A03GX8QUW7PXDE39N8UYF82VZ9PC8UFS3M7XA9 $ age-keygen -o key.txt - Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p` + Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p + + $ age-keygen -y key.txt + age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p` // Version can be set at link time to override debug.BuildInfo.Main.Version, // which is "(devel)" when building from within the module. See @@ -50,17 +60,21 @@ func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) } var ( - versionFlag bool - outFlag string + versionFlag, convertFlag bool + outFlag string ) flag.BoolVar(&versionFlag, "version", false, "print the version") + flag.BoolVar(&convertFlag, "y", false, "convert identities to recipients") flag.StringVar(&outFlag, "o", "", "output to `FILE` (default stdout)") flag.StringVar(&outFlag, "output", "", "output to `FILE` (default stdout)") flag.Parse() - if len(flag.Args()) != 0 { + if len(flag.Args()) != 0 && !convertFlag { log.Fatalf("age-keygen takes no arguments") } + if len(flag.Args()) > 1 && convertFlag { + log.Fatalf("Too many arguments") + } if versionFlag { if Version != "" { fmt.Println(Version) @@ -90,7 +104,21 @@ func main() { } } - generate(out) + in := os.Stdin + if inFile := flag.Arg(0); inFile != "" && inFile != "-" { + f, err := os.Open(inFile) + if err != nil { + log.Fatalf("Failed to open input file %q: %v", inFile, err) + } + defer f.Close() + in = f + } + + if convertFlag { + convert(in, out) + } else { + generate(out) + } } func generate(out *os.File) { @@ -107,3 +135,20 @@ func generate(out *os.File) { fmt.Fprintf(out, "# public key: %s\n", k.Recipient()) fmt.Fprintf(out, "%s\n", k) } + +func convert(in io.Reader, out io.Writer) { + ids, err := age.ParseIdentities(in) + if err != nil { + log.Fatalf("Failed to parse input: %v", err) + } + if len(ids) == 0 { + log.Fatalf("No identities found in the input") + } + for _, id := range ids { + id, ok := id.(*age.X25519Identity) + if !ok { + log.Fatalf("Internal error: unexpected identity type: %T", id) + } + fmt.Fprintf(out, "%s\n", id.Recipient()) + } +}