diff --git a/cmd/age-keygen/keygen.go b/cmd/age-keygen/keygen.go index 62e4522..91098e5 100644 --- a/cmd/age-keygen/keygen.go +++ b/cmd/age-keygen/keygen.go @@ -92,6 +92,20 @@ func main() { errorf("-pq cannot be used with -y") } + var converted []byte + if convertFlag { + in := os.Stdin + if inFile := flag.Arg(0); inFile != "" && inFile != "-" { + f, err := os.Open(inFile) + if err != nil { + errorf("failed to open input file %q: %v", inFile, err) + } + defer f.Close() + in = f + } + converted = convert(in) + } + out := os.Stdout if outFlag != "" { f, err := os.OpenFile(outFlag, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) @@ -106,18 +120,8 @@ func main() { out = f } - in := os.Stdin - if inFile := flag.Arg(0); inFile != "" && inFile != "-" { - f, err := os.Open(inFile) - if err != nil { - errorf("failed to open input file %q: %v", inFile, err) - } - defer f.Close() - in = f - } - if convertFlag { - convert(in, out) + writef(out, "%s", converted) } else { if fi, err := out.Stat(); err == nil && fi.Mode().IsRegular() && fi.Mode().Perm()&0004 != 0 { warning("writing secret key to a world-readable file") @@ -160,7 +164,7 @@ func writef(out io.Writer, format string, v ...any) { } } -func convert(in io.Reader, out io.Writer) { +func convert(in io.Reader) []byte { ids, err := age.ParseIdentities(in) if err != nil { errorf("failed to parse input: %v", err) @@ -168,17 +172,19 @@ func convert(in io.Reader, out io.Writer) { if len(ids) == 0 { errorf("no identities found in the input") } + var out []byte for _, id := range ids { switch id := id.(type) { case *age.X25519Identity: - writef(out, "%s\n", id.Recipient()) + out = append(out, id.Recipient().String()...) case *age.HybridIdentity: - writef(out, "%s\n", id.Recipient()) + out = append(out, id.Recipient().String()...) default: errorf("internal error: unexpected identity type: %T", id) } - + out = append(out, '\n') } + return out } func errorf(format string, v ...any) { diff --git a/cmd/age/testdata/hybrid.txt b/cmd/age/testdata/hybrid.txt index 3e8f971..b5524d1 100644 --- a/cmd/age/testdata/hybrid.txt +++ b/cmd/age/testdata/hybrid.txt @@ -39,9 +39,16 @@ age -d -i key.txt test.age cmp stdout input ! stderr . +! exec age-plugin-pq -identity -o mixed-plugin.txt mixed.txt +stderr 'identity #2 is not a post-quantum hybrid identity' +! exists mixed-plugin.txt + -- input -- test -- key.txt -- # created: 2025-11-17T13:27:37+01:00 # public key: age1pq1044e3hfmq2tltgck54m0gnpv8rfrwkdxjgl38psnwg8zfyv4uqdu63kk0u94ztt0q35k9dzuwgv405kxq6due2sre2kfsjftf7k2vz2mdpetku9pufy9d6ny6k5ss0cghpxdxrux0yd5xy40f8fu8ch9xn2rczyek2pqetqk0l9fqj0e3hfhkyfmcf9j6cpe5mf5stt9cymfq3xm276x4sramhn8srr42g78q9rkmqesevy33p4kdhkuggl99x9zx7j05uflx3s8q558xeaq9q9ctxqx52rzg6yffwykxngvxf7g3n83ddawagq2lnk59kcnexm54jrsmkjk9nemlge9v6p7fxu5z6yvq3ghpet8ww2c3h9fylvjx6j9njwz744376ap5r3xcpzh7qlk0kq3gknzfqhmxmjqypf6xz6geffdmy25apzsfr4ce0x825njkf62mgzx0x9n3zt0490u4dnt4468zse4zvd9sv0ycx2u2wsseuy4yymrg0nmfuxcwq9q0jykq7g5f59vunf95vnmcw336ksrh43fgapsd40u4227ymx56xtudtrte39twnq6fdfdj4p9pgx7pcqdk953za76fqcnvatx2cmywurag7ky4y8ecq5lvf25rydhxg6gmcn6vf78vtsqw79hp3mzv2u4v4jzn0nk50kl4gpv7k60cgz79c6k30jzy8dyetqtwg04tguxkv2p4hqj02yvsz2szaqdn9flu24rhth3v0t33023nwy27gt0x32eqpa5tpz6ygz3yzcsa3w889drpynzd5hlywuk9dwgafuax7upfnnxch4znrx4vactasg32u7vl886pxfswskk7wjgqkggrewtnaecfusl3nmg0xxh8e5nl5eft0xqwj5rk9ajn2j6yxehhgg5k6kpw5pk382hxmpu8wcd3rqx6m8905c7f8v0x2z9gyduxtqp2fdy9w2z4g2rrgu4d5cqes5tnwag007h2f326s3j9p6970xyd73rv5awx27zzezrax467c6vuye62ha88pzhw9xytjpezw3vg3l6x4k9rxwm9f6lwj6j7r9makg59rhgt0355s3jx9vw3raz5qdk5cnpny56q5h3dyd75suskuz63wmmfp9333tx4ry38vnh7jxy0j0cka99y7v2dmyjldjy0mqjetcefsmmg3pw0h9ll0rtskpmxz2cs8vmx6lu4v5rjtzpzgaqt46cwvnmhy5e3ye0tv6sre342lwg0tfd0yjkfz2haqc3hprpqf5ce8c9lx076dnk5cukn3tu38jgprrxt9sk9nqzt8grvkc4qzlg8jjf638v07ej9cf4eu48kphrgyjy83qm822xq8v9k22zwa7txr439x74j2f9ay7frkwtfdwn02enyx8stjpr4wgg25n5tj7uxyx0dqt3hnrqf7dcgt9fqnkjgemjfzyck6pawewptcayxm49sq3477f4cvm2p8fmg27rcmwm5l2vmzvdmymvkrr3aywu04y3zlhs7awu3r9u9lseheqpffvm2xf8y6w2qaj9g22pcu0sdsnqseusrxq9awru5lyqxnzftlslgpe3njegxkze98rveswp3u0xgx69wkg38vgp4k55lwwgv5l7ccv503mh9q23nud4xtm499xq276u40rxyrzaqfyuz2f3v5qr2vq00xs4g3ruynmq0c6nprvjcwqc5zrdn4cnzeeuwumjvqfjdpxr739n4cqcwytc8pjmfcj2qs3l2a8p5fzq7nvtng2jvvklx0x5ypnlkeuegsm587f6d4dkwel2es0lwwt88y2jdg8enjyph02tnjyndlx34h52fngjk96ggtu6vcxqkreqcxs4gwa8ww40t0kzlxfzyfhtjmfdm5fcrr5pt2xm4yslfynr0h9wkranhyadxf33kjn9xpg2fq5hnlq0 AGE-SECRET-KEY-PQ-16XDSLZ3XCSZ3236YJ5J0T9NAPLZTP96LJKQCVHJYUDTQVJR5J5PQTDPQCX +-- mixed.txt -- +AGE-SECRET-KEY-PQ-16XDSLZ3XCSZ3236YJ5J0T9NAPLZTP96LJKQCVHJYUDTQVJR5J5PQTDPQCX +AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0 diff --git a/cmd/age/testdata/keygen.txt b/cmd/age/testdata/keygen.txt index a34db82..9dbd6ae 100644 --- a/cmd/age/testdata/keygen.txt +++ b/cmd/age/testdata/keygen.txt @@ -23,3 +23,7 @@ stdout age1pq1 exec age-keygen -y key.txt stdout age1pq1 + +! exec age-keygen -y -o missing.txt missing +stderr 'failed to open input file' +! exists missing.txt diff --git a/extra/age-plugin-pq/plugin-pq.go b/extra/age-plugin-pq/plugin-pq.go index 19aab44..dc2c67f 100644 --- a/extra/age-plugin-pq/plugin-pq.go +++ b/extra/age-plugin-pq/plugin-pq.go @@ -66,6 +66,17 @@ func main() { errorf("too many arguments") } + in := os.Stdin + if inFile := flag.Arg(0); inFile != "" && inFile != "-" { + f, err := os.Open(inFile) + if err != nil { + errorf("failed to open input file %q: %v", inFile, err) + } + defer f.Close() + in = f + } + converted := convert(in) + out := os.Stdout if outFlag != "" { f, err := os.OpenFile(outFlag, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) @@ -82,18 +93,9 @@ func main() { if fi, err := out.Stat(); err == nil && fi.Mode().IsRegular() && fi.Mode().Perm()&0004 != 0 { warning("writing secret key to a world-readable file") } - - in := os.Stdin - if inFile := flag.Arg(0); inFile != "" && inFile != "-" { - f, err := os.Open(inFile) - if err != nil { - errorf("failed to open input file %q: %v", inFile, err) - } - defer f.Close() - in = f + if _, err := out.Write(converted); err != nil { + errorf("failed to write output: %v", err) } - - convert(in, out) return } @@ -123,11 +125,12 @@ func main() { os.Exit(p.Main()) } -func convert(in io.Reader, out io.Writer) { +func convert(in io.Reader) []byte { ids, err := age.ParseIdentities(in) if err != nil { errorf("failed to parse identities: %v", err) } + var out []byte for i, id := range ids { hybridID, ok := id.(*age.HybridIdentity) if !ok { @@ -137,8 +140,10 @@ func convert(in io.Reader, out io.Writer) { if err != nil { errorf("failed to decode identity #%d: %v", i+1, err) } - fmt.Fprintln(out, plugin.EncodeIdentity("pq", data)) + out = append(out, plugin.EncodeIdentity("pq", data)...) + out = append(out, '\n') } + return out } func errorf(format string, v ...any) {