package main import ( "bytes" "context" "fmt" "io/ioutil" "time" "git.sigsum.org/sigsum-go/pkg/requests" "git.sigsum.org/sigsum-go/pkg/types" "git.sigsum.org/sigsum-tools-go/pkg/client" "git.sigsum.org/sigsum-tools-go/pkg/policy" "git.sigsum.org/sigsum-tools-go/pkg/signatures" "git.sigsum.org/sigsum-tools-go/pkg/signatures/minisign" "git.sigsum.org/sigsum-tools-go/pkg/signatures/signify" "git.sigsum.org/sigsum-tools-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 { preimage, 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{ ShardHint: policy.ShardHint(), Preimage: *preimage, Signature: *sig, VerificationKey: *pub, DomainHint: optBundleDomainHint, } sd := types.Statement{ ShardHint: req.ShardHint, Checksum: *types.HashFn(req.Preimage[:]), } if !sd.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") } preimage, err := fileHash(args[0]) if err != nil { return fmt.Errorf("format: %v", err) } sd := types.Statement{ ShardHint: policy.ShardHint(), Checksum: *types.HashFn(preimage[:]), } fmt.Printf("%s", sd.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 }