From 8da382069f42f6d88d3abf914dd38d7e40a845bc Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Wed, 2 Mar 2022 23:16:43 +0100 Subject: initial commit --- cmd/sigsum/cmd.go | 131 +++++++++++++++++++++++++++++++++++++++ cmd/sigsum/main.go | 116 ++++++++++++++++++++++++++++++++++ cmd/sigsum/test/keys/signify.pub | 2 + cmd/sigsum/test/keys/signify.sec | 2 + cmd/sigsum/test/signify.sh | 55 ++++++++++++++++ 5 files changed, 306 insertions(+) create mode 100644 cmd/sigsum/cmd.go create mode 100644 cmd/sigsum/main.go create mode 100644 cmd/sigsum/test/keys/signify.pub create mode 100644 cmd/sigsum/test/keys/signify.sec create mode 100755 cmd/sigsum/test/signify.sh (limited to 'cmd/sigsum') diff --git a/cmd/sigsum/cmd.go b/cmd/sigsum/cmd.go new file mode 100644 index 0000000..70a1c51 --- /dev/null +++ b/cmd/sigsum/cmd.go @@ -0,0 +1,131 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "time" + + "git.sigsum.org/sigsum-lib-go/pkg/requests" + "git.sigsum.org/sigsum-lib-go/pkg/types" + "git.sigsum.org/sigsum-tool-go/pkg/client" + "git.sigsum.org/sigsum-tool-go/pkg/policy" + "git.sigsum.org/sigsum-tool-go/pkg/signatures" + "git.sigsum.org/sigsum-tool-go/pkg/signatures/minisign" + "git.sigsum.org/sigsum-tool-go/pkg/signatures/signify" + "git.sigsum.org/sigsum-tool-go/pkg/signatures/ssh" +) + +func cmdVerify(args []string, policy policy.Policy, optVerifyType, optVerifyKey string) error { + return fmt.Errorf("TODO") +} + +func cmdBundle(args []string, policy policy.Policy, optBundleType, optBundleKey, optBundleDomainHint string) error { + if len(args) == 0 { + return fmt.Errorf("bundle: need at least one file") + } + + var parser signatures.Parser + switch optBundleType { + case "signify": + parser = &signify.Parser{} + case "minisign": + parser = &minisign.Parser{} + case "ssh": + parser = &ssh.Parser{} + default: + return fmt.Errorf("bundle: invalid key type %q", optBundleType) + } + + b, err := ioutil.ReadFile(optBundleKey) + if err != nil { + return fmt.Errorf("bundle: failed reading file %q: %v", optBundleKey, err) + } + pub, err := parser.PublicKey(bytes.NewBuffer(b)) + if err != nil { + return fmt.Errorf("bundle: %v", err) + } + // TODO: check that domain hint is valid for public key + + var reqs []requests.Leaf + for _, path := range args { + checksum, err := fileHash(path) + if err != nil { + return fmt.Errorf("bundle: %v", err) + } + + sigPath := path + parser.SignatureSuffix() + b, err := ioutil.ReadFile(sigPath) + if err != nil { + return fmt.Errorf("bundle: failed reading file %q: %v", sigPath, err) + } + sig, err := parser.Signature(bytes.NewBuffer(b)) + if err != nil { + return fmt.Errorf("bundle: %v", err) + } + + req := requests.Leaf{ + Statement: types.Statement{ + ShardHint: policy.ShardHint(), + Checksum: *checksum, + }, + Signature: *sig, + VerificationKey: *pub, + DomainHint: optBundleDomainHint, + } + if !req.Statement.Verify(&req.VerificationKey, &req.Signature) { + return fmt.Errorf("bundle: invalid signature for file %q", path) + } + reqs = append(reqs, req) + } + + sc := client.NewSubmitClient(policy) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer cancel() + bundles, err := sc.AddLeaves(ctx, reqs) + if err != nil { + return fmt.Errorf("bundle: %v", err) + } + + // TODO: verify bundles + // TODO: write to files + fmt.Printf("got %d bundles\n", len(bundles)) + return nil +} + +func cmdFormat(args []string, policy policy.Policy) error { + if len(args) != 1 { + return fmt.Errorf("format: need exactly one file") + } + + checksum, err := fileHash(args[0]) + if err != nil { + return fmt.Errorf("format: %v", err) + } + stm := types.Statement{ + ShardHint: policy.ShardHint(), + Checksum: *checksum, + } + + fmt.Printf("%s", stm.ToBinary()) + return nil +} + +func cmdNamespace(args []string, policy policy.Policy) error { + if len(args) != 0 { + return fmt.Errorf("namespace: got trailing arguments") + } + + fmt.Printf("tree_leaf:v0:%d@sigsum.org", policy.ShardHint()) + return nil +} + +// TODO: don't read full file into memory at once +func fileHash(path string) (*types.Hash, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("failed reading file %q", path) + } + return types.HashFn(b), nil +} diff --git a/cmd/sigsum/main.go b/cmd/sigsum/main.go new file mode 100644 index 0000000..460eba3 --- /dev/null +++ b/cmd/sigsum/main.go @@ -0,0 +1,116 @@ +// package main provides a tool named `sigsum`. +// +// Build as follows: +// +// $ go build -ldflags="-X 'main.someVersion=git commit $(git rev-list -1 HEAD)'" +// +// Install as follows: +// +// $ go install -ldflags="-X 'main.someVersion=git commit $(git rev-list -1 HEAD)'" +// +package main + +import ( + "flag" + "fmt" + "log" + "os" + + "git.sigsum.org/sigsum-tool-go/pkg/policy" +) + +const usage = `sigsum version %s + +Usage: + sigsum help + Output usage message. + + sigsum verify -t TYPE -k PUBLIC_KEY FILE + Verify that a file's signed checksum is public and valid. + -t, --type Signature format (Available options: signify, minisign, ssh) + -k, --key Path to a public key. + + sigsum bundle -t TYPE -k PUBLIC_KEY -d DOMAIN_HINT FILE... + Perform logging request(s) and write inclusion proof bundle(s). + -t, --type Signature format (Available options: signify, minisign, ssh) + -k, --key Path to a public key. + -d, --domain-hint Domain name that is aware of the public key. + + sigsum format FILE + Output bytes to be Ed25519-signed. + + sigsum namespace + Output namespace to be used in SSH signing context. + +Transparency log proofs and signatures must be located at $FILE.sigsum.v0. +Signatures must be located at $FILE.{sig,minisig}, depending on -t TYPE. + +` + +var ( + optBundleType, optBundleKey, optBundleDomainHint string + optVerifyType, optVerifyKey string + + someVersion = "unknown" +) + +func main() { + log.SetFlags(0) + + var err error + var defaultPolicy policy.DefaultPolicy + switch cmd := parseCommand(); cmd.Name() { + case "help": + cmd.Usage() + case "verify": + err = cmdVerify(cmd.Args(), &defaultPolicy, optVerifyType, optVerifyKey) + case "bundle": + err = cmdBundle(cmd.Args(), &defaultPolicy, optBundleType, optBundleKey, optBundleDomainHint) + case "format": + err = cmdFormat(cmd.Args(), &defaultPolicy) + case "namespace": + err = cmdNamespace(cmd.Args(), &defaultPolicy) + default: + err = fmt.Errorf("invalid command %q, try %q", cmd.Name(), "sigsum help") + } + + if err != nil { + log.Printf("%s", err) + os.Exit(1) + } +} + +func parseCommand() (fs *flag.FlagSet) { + args := os.Args + if len(args) < 2 { + args = append(args, "") + } + defer func() { + registerOptions(fs) + fs.Usage = func() { + log.Printf(usage, someVersion) + } + fs.Parse(args) + }() + + fs = flag.NewFlagSet(args[1], flag.ExitOnError) + args = args[2:] + return +} + +func registerOptions(fs *flag.FlagSet) { + switch cmd := fs.Name(); cmd { + case "verify": + registerStringOption(fs, &optVerifyType, "t", "type", "") + registerStringOption(fs, &optVerifyKey, "k", "key", "") + case "bundle": + registerStringOption(fs, &optBundleType, "t", "type", "") + registerStringOption(fs, &optBundleKey, "k", "key", "") + registerStringOption(fs, &optBundleDomainHint, "d", "domain-hint", "") + } +} + +func registerStringOption(fs *flag.FlagSet, opt *string, short, long, value string) { + fs.StringVar(opt, short, value, "") + fs.StringVar(opt, long, value, "") +} diff --git a/cmd/sigsum/test/keys/signify.pub b/cmd/sigsum/test/keys/signify.pub new file mode 100644 index 0000000..742a66a --- /dev/null +++ b/cmd/sigsum/test/keys/signify.pub @@ -0,0 +1,2 @@ +untrusted comment: signify public key +RWQhuW/GnP7W13NSC8qzkpnB1BJXk96/GhaWe6f/OpBvMRHFdwuUIYGb diff --git a/cmd/sigsum/test/keys/signify.sec b/cmd/sigsum/test/keys/signify.sec new file mode 100644 index 0000000..57cdf84 --- /dev/null +++ b/cmd/sigsum/test/keys/signify.sec @@ -0,0 +1,2 @@ +untrusted comment: signify secret key +RWRCSwAAACrUdp2uXyio8Rdwv0W6PLGiUQei6JeOZAYhuW/GnP7W1655JdycJo4tbOh/ba1OxA7QyVSdNFBs5SyF4eM5yIE98xhTAtizBDxki1Y3sqcFvWFH8ZlKzRjY8rUrTYaaCQE= diff --git a/cmd/sigsum/test/signify.sh b/cmd/sigsum/test/signify.sh new file mode 100755 index 0000000..8e86e8d --- /dev/null +++ b/cmd/sigsum/test/signify.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -e +trap cleanup EXIT + +pass=1234 +priv=keys/signify.sec +pub=keys/signify.pub +domain_hint=_sigsum_v0.test-only.rgdd.se +msg=msg-$(date +%s) +num_msg=3 + +function cleanup() { + set +e + + rm -f sigsum + for i in $(seq 1 $num_msg); do + rm -f $msg-$i{,.trunnel,.sig} + done + + exit +} + +go build ../ + +files="" +for i in $(seq 1 $num_msg); do + echo $msg-$i > $msg-$i + if ! ./sigsum format $msg-$i > $msg-$i.trunnel; then + echo "[FAIL] format for $num_msg signify message(s)" >&2 + exit 1 + fi + if ! echo $pass | signify-openbsd -Ss $priv -m $msg-$i.trunnel -x $msg-$i.sig; then + echo "[FAIL] sign for $num_msg signify message(s)" >&2 + exit 1 + fi + files=$(echo -n $files $msg-$i) +done + +echo "[PASS] format for $num_msg signify message(s)" >&2 +echo "[PASS] sign for $num_msg signify message(s)" >&2 + +if ! ./sigsum bundle -t signify -k $pub -d $domain_hint $files; then + echo "[FAIL] bundle for $num_msg signify message(s)" >&2 + exit 1 +fi + +echo "[PASS] bundle for $num_msg signify message(s)" >&2 + +if ! ./sigsum verify -t signify -k $pub $files; then + echo "[FAIL] verify for $num_msg signify message(s)" >&2 + exit 1 +fi + +echo "[PASS] verify for $num_msg signify message(s)" >&2 -- cgit v1.2.3