From 725bc7d8278b6549320df12f23aab3d983f27ce1 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sat, 5 Dec 2020 17:15:53 -0700 Subject: [PATCH] Thanks to jrick for the diff to add clearsig support! - go mod tidy - add priv key for adent so others can add tests --- go.sum | 2 - main.go | 116 +++++++++++++++++++++++++++---------------- run_test.sh | 3 ++ test/priv/adent.key | 57 +++++++++++++++++++++ test/uptime2.txt.asc | 15 ++++++ 5 files changed, 149 insertions(+), 44 deletions(-) create mode 100644 test/priv/adent.key create mode 100644 test/uptime2.txt.asc diff --git a/go.sum b/go.sum index d6c0e34..1d5d29d 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w= -golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/main.go b/main.go index 4c09281..f3d902f 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,16 @@ package main import ( + "bytes" "flag" "fmt" "io" + "io/ioutil" "os" "path/filepath" - "strings" "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/clearsign" "suah.dev/protect" ) @@ -23,59 +25,89 @@ func errExit(err error) { } } +var flags struct { + sig, file, pub string +} + func main() { - var sig, file, pub string - flag.StringVar(&sig, "sig", "", "path to signature file") - flag.StringVar(&file, "file", "", "path to file") - flag.StringVar(&pub, "pub", "", "path to pub file") + _ = protect.Pledge("stdio unveil rpath") + + flag.StringVar(&flags.sig, "sig", "", + "path to signature file; if file contains cleartext message\n"+ + "with signature, -file must be unset") + flag.StringVar(&flags.file, "file", "", + "path to unsigned message file; incompatible with cleartext\n"+ + "signatures") + flag.StringVar(&flags.pub, "pub", "", "path to pubkey file") flag.Parse() - _ = protect.Pledge("stdio tty unveil rpath") - - _ = protect.Unveil(sig, "r") - _ = protect.Unveil(file, "r") - _ = protect.Unveil(pub, "r") + _ = protect.Unveil(flags.sig, "r") + _ = protect.Unveil(flags.file, "r") + _ = protect.Unveil(flags.pub, "r") + ext := filepath.Ext(flags.sig) + var signoext string + if flags.file == "" && ext != "" { + signoext = flags.sig[:len(flags.sig)-len(ext)] + _ = protect.Unveil(signoext, "r") + } _ = protect.UnveilBlock() - if sig != "" && file == "" { - // Check for a 'file' with the .sig extensions removed - bn := strings.TrimSuffix(sig, filepath.Ext(sig)) - if _, err := os.Stat(bn); err == nil { - file = bn - } - } - - fPub, err := os.Open(pub) + pubFi, err := os.Open(flags.pub) errExit(err) - - fFile, err := os.Open(file) - errExit(err) - - fSig, err := os.Open(sig) - errExit(err) - - defer fPub.Close() - defer fSig.Close() - defer fFile.Close() - - kr, err := openpgp.ReadArmoredKeyRing(fPub) + defer pubFi.Close() + kr, err := openpgp.ReadArmoredKeyRing(pubFi) if err != nil { - fmt.Printf("Can't parse public key '%s'\n%s", pub, err) + fmt.Printf("Can't parse public key %q\n%s\n", flags.pub, err) os.Exit(1) } - var ent *openpgp.Entity - - switch { - case strings.HasSuffix(sig, ".sig"), strings.HasSuffix(sig, ".gpg"): - ent, err = openpgp.CheckDetachedSignature(kr, fFile, fSig) - case strings.HasSuffix(sig, ".asc"): - ent, err = openpgp.CheckArmoredDetachedSignature(kr, fFile, fSig) - default: - // Try to open as an armored file if we don't know the extension - ent, err = openpgp.CheckArmoredDetachedSignature(kr, fFile, fSig) + var sig, message io.Reader + var clearsigBlock *clearsign.Block + var armored bool + clearsigned := func(data []byte) bool { + clearsigBlock, _ = clearsign.Decode(data) + return clearsigBlock != nil } + sigBytes, err := ioutil.ReadFile(flags.sig) + errExit(err) + switch { + case clearsigned(sigBytes): + if flags.file != "" { + fmt.Printf("-file is incompatible with cleartext signatures\n") + os.Exit(1) + } + message = bytes.NewReader(clearsigBlock.Bytes) + sig = clearsigBlock.ArmoredSignature.Body + armored = false // Body provides decoded signature + case flags.file == "": + // Check for a message file with the .sig extensions removed + flags.file = signoext + fallthrough + default: + messageFi, err := os.Open(flags.file) + if os.IsNotExist(err) { + fmt.Printf("signature %s does not provide cleartext, and no "+ + "message %s found\n", flags.sig, flags.file) + os.Exit(1) + } + errExit(err) + defer messageFi.Close() + + message = messageFi + sig = bytes.NewReader(sigBytes) + // Unless signature file uses .gpg or .sig extensions, read + // ascii armored input. This covers .asc signatures, and + // assumes armoring if the extension is otherwise unknown. + armored = ext != ".gpg" && ext != ".sig" + } + + var ent *openpgp.Entity + if armored { + ent, err = openpgp.CheckArmoredDetachedSignature(kr, message, sig) + } else { + ent, err = openpgp.CheckDetachedSignature(kr, message, sig) + } errExit(err) for _, id := range ent.Identities { diff --git a/run_test.sh b/run_test.sh index 4133610..9368f74 100755 --- a/run_test.sh +++ b/run_test.sh @@ -16,6 +16,9 @@ printf "Testing detached non-armor (sig)\t" printf "Testing just having a sig (nofile)\t" ./ogvt -sig test/uptime.txt.sig -pub test/adent.pub >/dev/null && echo "OK" || echo "FAIL" +printf "Testing clearsig file (clear-asc)\t" +./ogvt -sig test/uptime2.txt.asc -pub test/adent.pub >/dev/null && echo "OK" || echo "FAIL" + printf "Testing bad sig file\t\t\t" ./ogvt -file test/uptime.txt -sig test/bad.sig -pub test/adent.pub >/dev/null && echo "FAIL" || echo "OK" diff --git a/test/priv/adent.key b/test/priv/adent.key new file mode 100644 index 0000000..2c475f2 --- /dev/null +++ b/test/priv/adent.key @@ -0,0 +1,57 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=Z5Pn +-----END PGP PRIVATE KEY BLOCK----- diff --git a/test/uptime2.txt.asc b/test/uptime2.txt.asc new file mode 100644 index 0000000..7c035a7 --- /dev/null +++ b/test/uptime2.txt.asc @@ -0,0 +1,15 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + + 7:48AM up 37 mins, 0 users, load averages: 0.17, 0.46, 0.44 +-----BEGIN PGP SIGNATURE----- + +iQEzBAEBCgAdFiEEhaN94Du+mjAZp+OkO8VGry5nBbcFAl/MIP4ACgkQO8VGry5n +BbdWmgf+OdLXesitBnGQJNvdVsaRr+uC067c7LUNV2Piq1S+hxAdLkVGJNhReCrK +8WaB+TqyvaFZvCLipddnS1wt81DI70J5M/1mcjIwhZye7PnRbP5bWgV7ncgaYvS/ +gVrH3ffVywWWPap0nz23P1zt1GaObmUCUUhk2DsLjF52Z7ll8JiPQuAtg8ir3cfM +VJLFKDWbQFQgZxZE6glAAH+BUYdWxAVTxrurti7UDgule44RG++KLcBw9+M51QNw +OHDujTk8mBfNood61SVEH9MAS+aotghTaAhm8sOd2eedthlcGpCB1+B4DvpHckQb +4EA9WvmGyx+j0KwLMnHQ2dFiYEVseg== +=KIsW +-----END PGP SIGNATURE-----