aboutsummaryrefslogtreecommitdiff
path: root/cmd/sigsum
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus@mullvad.net>2022-03-02 23:16:43 +0100
committerRasmus Dahlberg <rasmus@mullvad.net>2022-03-02 23:17:48 +0100
commit8da382069f42f6d88d3abf914dd38d7e40a845bc (patch)
tree780e8297ee3905ab662c6c88cb8bf33f0717c90c /cmd/sigsum
initial commit
Diffstat (limited to 'cmd/sigsum')
-rw-r--r--cmd/sigsum/cmd.go131
-rw-r--r--cmd/sigsum/main.go116
-rw-r--r--cmd/sigsum/test/keys/signify.pub2
-rw-r--r--cmd/sigsum/test/keys/signify.sec2
-rwxr-xr-xcmd/sigsum/test/signify.sh55
5 files changed, 306 insertions, 0 deletions
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