ogvt/main.go

141 lines
3.3 KiB
Go
Raw Normal View History

2020-02-06 08:09:47 -07:00
package main
import (
"bytes"
2020-02-06 08:09:47 -07:00
"flag"
"fmt"
"io"
"io/ioutil"
"log"
2020-02-06 08:09:47 -07:00
"os"
2020-11-16 08:27:40 -07:00
"path/filepath"
"text/tabwriter"
2020-02-06 08:09:47 -07:00
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/clearsign"
"github.com/ProtonMail/go-crypto/openpgp/packet"
2020-05-09 07:39:43 -06:00
"suah.dev/protect"
2020-02-06 08:09:47 -07:00
)
func errExit(err error) {
2020-02-06 08:09:47 -07:00
if err != nil {
if err == io.EOF {
fmt.Println("invalid signature file")
2020-02-15 17:21:31 -07:00
} else {
fmt.Println(err)
}
2020-02-06 08:09:47 -07:00
os.Exit(1)
}
}
var flags struct {
sig, file, pub string
}
2020-02-06 08:09:47 -07:00
func main() {
_ = 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")
2020-02-06 08:09:47 -07:00
flag.Parse()
2021-01-13 07:41:25 -07:00
if len(os.Args) == 1 {
flag.PrintDefaults()
os.Exit(1)
}
_ = 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")
2020-11-16 08:27:40 -07:00
}
_ = protect.UnveilBlock()
2020-11-16 08:27:40 -07:00
pubFi, err := os.Open(flags.pub)
errExit(err)
kr, err := openpgp.ReadArmoredKeyRing(pubFi)
if err != nil {
fmt.Printf("Can't parse public key %q\n%s\n", flags.pub, err)
os.Exit(1)
}
if flags.sig == "" && flags.file == "" {
for _, ent := range kr {
for name := range ent.Identities {
fmt.Printf("%q (%X)\n", name, ent.PrimaryKey.Fingerprint)
}
}
return
}
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)
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"
}
2020-02-06 08:09:47 -07:00
var ent *openpgp.Entity
var pkt *packet.Config
if armored {
ent, err = openpgp.CheckArmoredDetachedSignature(kr, message, sig, pkt)
} else {
ent, err = openpgp.CheckDetachedSignature(kr, message, sig, pkt)
}
errExit(err)
2020-02-06 08:09:47 -07:00
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
2020-02-06 08:09:47 -07:00
for _, id := range ent.Identities {
_, err := fmt.Fprintf(w, "%q\t(%X)\n", id.Name, ent.PrimaryKey.Fingerprint)
if err != nil {
log.Println(err)
}
2020-02-06 08:09:47 -07:00
}
err = w.Flush()
errExit(err)
2020-02-06 08:09:47 -07:00
fmt.Println("Signature OK.")
}