2016-10-06 22:01:11 -07:00
|
|
|
// sigtool.go -- Tool to generate, manage Ed25519 keys and
|
|
|
|
// signatures.
|
|
|
|
//
|
|
|
|
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
|
|
//
|
2018-02-28 22:01:01 -06:00
|
|
|
// Licensing Terms: GPLv2
|
2016-10-06 22:01:11 -07:00
|
|
|
//
|
|
|
|
// If you need a commercial license for this work, please contact
|
|
|
|
// the author.
|
|
|
|
//
|
|
|
|
// This software does not come with any express or implied
|
|
|
|
// warranty; it is provided "as is". No claim is made to its
|
|
|
|
// suitability for any purpose.
|
|
|
|
|
|
|
|
package main
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2016-10-06 22:01:11 -07:00
|
|
|
import (
|
2018-02-28 22:01:01 -06:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
2019-10-09 14:52:34 -07:00
|
|
|
"strings"
|
2018-02-28 22:01:01 -06:00
|
|
|
|
|
|
|
"github.com/opencoff/go-utils"
|
2019-06-20 10:51:33 +05:30
|
|
|
flag "github.com/opencoff/pflag"
|
|
|
|
"github.com/opencoff/sigtool/sign"
|
2016-10-06 22:01:11 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
var Z string = path.Base(os.Args[0])
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
func main() {
|
|
|
|
|
|
|
|
var ver, help bool
|
|
|
|
|
|
|
|
mf := flag.NewFlagSet(Z, flag.ExitOnError)
|
|
|
|
mf.SetInterspersed(false)
|
|
|
|
mf.BoolVarP(&ver, "version", "v", false, "Show version info and exit")
|
|
|
|
mf.BoolVarP(&help, "help", "h", false, "Show help info exit")
|
|
|
|
mf.Parse(os.Args[1:])
|
|
|
|
|
|
|
|
if ver {
|
2019-10-18 15:42:08 -07:00
|
|
|
fmt.Printf("%s - %s [%s; %s]\n", Z, ProductVersion, RepoVersion, Buildtime)
|
2018-05-01 23:59:19 -05:00
|
|
|
os.Exit(0)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if help {
|
|
|
|
usage(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
args := mf.Args()
|
|
|
|
if len(args) < 1 {
|
|
|
|
warn("Insufficient arguments. Try '%s -h'", Z)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2019-10-09 14:52:34 -07:00
|
|
|
cmds := map[string]func(args []string){
|
|
|
|
"generate": gen,
|
|
|
|
"sign": signify,
|
|
|
|
"verify": verify,
|
|
|
|
"encrypt": encrypt,
|
|
|
|
"decrypt": decrypt,
|
2018-05-01 23:59:19 -05:00
|
|
|
|
2019-10-09 14:52:34 -07:00
|
|
|
"help": func(_ []string) {
|
|
|
|
usage(0)
|
|
|
|
},
|
|
|
|
}
|
2018-05-01 23:59:19 -05:00
|
|
|
|
2019-10-09 14:52:34 -07:00
|
|
|
words := make([]string, 0, len(cmds))
|
|
|
|
for k := range cmds {
|
|
|
|
words = append(words, k)
|
|
|
|
}
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2019-10-09 14:52:34 -07:00
|
|
|
ab := utils.Abbrev(words)
|
|
|
|
canon, ok := ab[strings.ToLower(args[0])]
|
|
|
|
if !ok {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("Unknown command %s", args[0])
|
|
|
|
}
|
2019-10-09 14:52:34 -07:00
|
|
|
|
|
|
|
cmd := cmds[canon]
|
|
|
|
if cmd == nil {
|
|
|
|
die("can't map command %s", canon)
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd(args[1:])
|
2018-05-01 23:59:19 -05:00
|
|
|
}
|
2016-10-06 22:01:11 -07:00
|
|
|
|
|
|
|
// Run the generate command
|
2018-05-01 23:59:19 -05:00
|
|
|
func gen(args []string) {
|
|
|
|
|
|
|
|
var pw, help, force bool
|
|
|
|
var comment string
|
|
|
|
var envpw string
|
|
|
|
|
|
|
|
fs := flag.NewFlagSet("generate", flag.ExitOnError)
|
|
|
|
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
2019-06-20 10:51:33 +05:30
|
|
|
fs.BoolVarP(&pw, "password", "p", false, "Ask for passphrase to encrypt the private key")
|
|
|
|
fs.StringVarP(&comment, "comment", "c", "", "Use `C` as the text comment for the keys")
|
|
|
|
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
|
|
|
fs.BoolVarP(&force, "force", "F", false, "Overwrite the output file if it exists")
|
2018-05-01 23:59:19 -05:00
|
|
|
|
|
|
|
fs.Parse(args)
|
|
|
|
|
|
|
|
if help {
|
|
|
|
fs.SetOutput(os.Stdout)
|
|
|
|
fmt.Printf(`%s generate|gen|g [options] file-prefix
|
|
|
|
|
|
|
|
Generate a new Ed25519 public+private key pair and write public key to
|
|
|
|
FILE-PREFIX.pub and private key to FILE-PREFIX.key.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
`, Z)
|
|
|
|
fs.PrintDefaults()
|
|
|
|
os.Exit(0)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
args = fs.Args()
|
|
|
|
if len(args) < 1 {
|
|
|
|
die("Insufficient arguments to 'generate'. Try '%s generate -h' ..", Z)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
bn := args[0]
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if exists(bn) && !force {
|
|
|
|
die("Public/Private key files (%s.key, %s.pub) exist. Won't overwrite!", bn, bn)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
var pws string
|
2018-02-28 22:01:01 -06:00
|
|
|
var err error
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if len(envpw) > 0 {
|
|
|
|
pws = os.Getenv(envpw)
|
|
|
|
} else if pw {
|
|
|
|
pws, err = utils.Askpass("Enter passphrase for private key", true)
|
2018-02-28 22:01:01 -06:00
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kp, err := sign.NewKeypair()
|
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
err = kp.Serialize(bn, comment, pws)
|
2018-02-28 22:01:01 -06:00
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
}
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
// Run the 'sign' command.
|
|
|
|
func signify(args []string) {
|
|
|
|
var pw, help bool
|
|
|
|
var output string
|
|
|
|
var envpw string
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
fs := flag.NewFlagSet("sign", flag.ExitOnError)
|
|
|
|
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
2019-06-20 10:51:33 +05:30
|
|
|
fs.BoolVarP(&pw, "password", "p", false, "Ask for passphrase to decrypt the private key")
|
|
|
|
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
|
|
|
fs.StringVarP(&output, "output", "o", "", "Write signature to file `F`")
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
fs.Parse(args)
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if help {
|
|
|
|
fs.SetOutput(os.Stdout)
|
|
|
|
fmt.Printf(`%s sign|s [options] privkey file
|
|
|
|
|
|
|
|
Sign FILE with a Ed25519 private key PRIVKEY and write signature to FILE.sig
|
|
|
|
|
|
|
|
Options:
|
|
|
|
`, Z)
|
|
|
|
fs.PrintDefaults()
|
|
|
|
os.Exit(0)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
args = fs.Args()
|
|
|
|
if len(args) < 2 {
|
|
|
|
die("Insufficient arguments to 'sign'. Try '%s sign -h' ..", Z)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
kn := args[0]
|
|
|
|
fn := args[1]
|
|
|
|
outf := fmt.Sprintf("%s.sig", fn)
|
|
|
|
|
|
|
|
var pws string
|
2018-02-28 22:01:01 -06:00
|
|
|
var err error
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if len(envpw) > 0 {
|
|
|
|
pws = os.Getenv(envpw)
|
|
|
|
} else if pw {
|
|
|
|
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
2018-02-28 22:01:01 -06:00
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if len(output) > 0 {
|
|
|
|
outf = output
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2019-10-14 09:46:03 -07:00
|
|
|
sk, err := sign.ReadPrivateKey(kn, pws)
|
2018-02-28 22:01:01 -06:00
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2019-10-14 09:46:03 -07:00
|
|
|
sig, err := sk.SignFile(fn)
|
2018-02-28 22:01:01 -06:00
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
sigo, err := sig.Serialize(fmt.Sprintf("input=%s", fn))
|
|
|
|
|
|
|
|
var fd io.Writer = os.Stdout
|
|
|
|
|
|
|
|
if outf != "-" {
|
|
|
|
fdx, err := os.OpenFile(outf, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
die("can't create output file %s: %s", outf, err)
|
|
|
|
}
|
|
|
|
defer fdx.Close()
|
|
|
|
fd = fdx
|
|
|
|
}
|
2018-02-28 22:01:01 -06:00
|
|
|
|
|
|
|
fd.Write(sigo)
|
2016-10-06 22:01:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify signature on a given file
|
2018-05-01 23:59:19 -05:00
|
|
|
func verify(args []string) {
|
|
|
|
var help, quiet bool
|
|
|
|
|
|
|
|
fs := flag.NewFlagSet("verify", flag.ExitOnError)
|
|
|
|
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
2019-06-20 10:51:33 +05:30
|
|
|
fs.BoolVarP(&quiet, "quiet", "q", false, "Don't show any output; exit with status code only")
|
2018-05-01 23:59:19 -05:00
|
|
|
|
|
|
|
fs.Parse(args)
|
|
|
|
|
|
|
|
if help {
|
|
|
|
fs.SetOutput(os.Stdout)
|
|
|
|
fmt.Printf(`%s verify|v [options] pubkey sig file
|
|
|
|
|
|
|
|
Verify an Ed25519 signature in SIG of FILE using a public key PUBKEY.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
`, Z)
|
|
|
|
fs.PrintDefaults()
|
|
|
|
os.Exit(0)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
args = fs.Args()
|
|
|
|
if len(args) < 3 {
|
|
|
|
die("Insufficient arguments to 'verify'. Try '%s verify -h' ..", Z)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
pn := args[0]
|
|
|
|
sn := args[1]
|
|
|
|
fn := args[2]
|
2018-02-28 22:01:01 -06:00
|
|
|
|
|
|
|
sig, err := sign.ReadSignature(sn)
|
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("Can't read signature '%s': %s", sn, err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pk, err := sign.ReadPublicKey(pn)
|
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if !sig.IsPKMatch(pk) {
|
|
|
|
die("Wrong public key '%s' for verifying '%s'", pn, sn)
|
|
|
|
}
|
|
|
|
|
|
|
|
ok, err := pk.VerifyFile(fn, sig)
|
|
|
|
if err != nil {
|
2018-05-01 23:59:19 -05:00
|
|
|
die("%s", err)
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
exit := 0
|
|
|
|
if !ok {
|
|
|
|
exit = 1
|
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if !quiet {
|
2018-02-28 22:01:01 -06:00
|
|
|
if ok {
|
|
|
|
fmt.Printf("%s: Signature %s verified\n", fn, sn)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("%s: Signature %s verification failure\n", fn, sn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Exit(exit)
|
2016-10-06 22:01:11 -07:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
func usage(c int) {
|
|
|
|
x := fmt.Sprintf(`%s is a tool to generate, sign and verify files with Ed25519 signatures.
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
Usage: %s [global-options] command [options] arg [args..]
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
Global options:
|
|
|
|
-h, --help Show help and exit
|
|
|
|
-v, --version Show version info and exit.
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
Commands:
|
|
|
|
generate, g Generate a new Ed25519 keypair
|
|
|
|
sign, s Sign a file with a private key
|
|
|
|
verify, v Verify a signature against a file and a public key
|
2019-10-17 14:29:01 -07:00
|
|
|
encrypt, e Encrypt an input file to one or more recipients
|
|
|
|
decrypt, d Decrypt a file with a private key
|
2018-05-01 23:59:19 -05:00
|
|
|
`, Z, Z)
|
|
|
|
|
|
|
|
os.Stdout.Write([]byte(x))
|
|
|
|
os.Exit(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return true if $bn.key or $bn.pub exist; false otherwise
|
|
|
|
func exists(bn string) bool {
|
|
|
|
pk := bn + ".pub"
|
|
|
|
sk := bn + ".key"
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
if _, err := os.Stat(pk); err == nil {
|
|
|
|
return true
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
2018-05-01 23:59:19 -05:00
|
|
|
if _, err := os.Stat(sk); err == nil {
|
|
|
|
return true
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
return false
|
|
|
|
}
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
// die with error
|
|
|
|
func die(f string, v ...interface{}) {
|
|
|
|
warn(f, v...)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2018-02-28 22:01:01 -06:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
func warn(f string, v ...interface{}) {
|
|
|
|
z := fmt.Sprintf("%s: %s", os.Args[0], f)
|
|
|
|
s := fmt.Sprintf(z, v...)
|
|
|
|
if n := len(s); s[n-1] != '\n' {
|
|
|
|
s += "\n"
|
2018-02-28 22:01:01 -06:00
|
|
|
}
|
2016-10-06 22:01:11 -07:00
|
|
|
|
2018-05-01 23:59:19 -05:00
|
|
|
os.Stderr.WriteString(s)
|
|
|
|
os.Stderr.Sync()
|
2016-10-06 22:01:11 -07:00
|
|
|
}
|
2016-10-06 22:06:41 -07:00
|
|
|
|
2019-10-18 15:42:08 -07:00
|
|
|
// This will be filled in by "build"
|
|
|
|
var RepoVersion string = "UNDEFINED"
|
|
|
|
var Buildtime string = "UNDEFINED"
|
|
|
|
var ProductVersion string = "UNDEFINED"
|
|
|
|
|
2018-02-28 22:01:01 -06:00
|
|
|
// vim: ft=go:sw=8:ts=8:noexpandtab:tw=98:
|