Thanks to jrick for the diff to add clearsig support!

- go mod tidy
- add priv key for adent so others can add tests
This commit is contained in:
Aaron Bieber 2020-12-05 17:15:53 -07:00
parent 0221349dcc
commit 725bc7d827
5 changed files with 149 additions and 44 deletions

2
go.sum
View File

@ -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=

116
main.go
View File

@ -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 {

View File

@ -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"

57
test/priv/adent.key Normal file
View File

@ -0,0 +1,57 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
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
=Z5Pn
-----END PGP PRIVATE KEY BLOCK-----

15
test/uptime2.txt.asc Normal file
View File

@ -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-----