From e18d36ebae30536c77c61cd5da123991e0ca1629 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Sun, 31 Dec 2023 09:39:25 +0100 Subject: Add drafty prototype --- cmd/silent-ctnode/main.go | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 cmd/silent-ctnode/main.go (limited to 'cmd/silent-ctnode/main.go') diff --git a/cmd/silent-ctnode/main.go b/cmd/silent-ctnode/main.go new file mode 100644 index 0000000..fec088c --- /dev/null +++ b/cmd/silent-ctnode/main.go @@ -0,0 +1,127 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "strings" + + "rgdd.se/silent-ct/internal/flagopt" + "rgdd.se/silent-ct/internal/ioutil" + "rgdd.se/silent-ct/internal/logger" + "rgdd.se/silent-ct/pkg/crtutil" + "rgdd.se/silent-ct/pkg/policy" + "rgdd.se/silent-ct/pkg/submission" +) + +const usage = ` +A utility that generates a submission of one or more certificate chains. +The generated submission is protected by a message authentication code. + +Usage: + + silent-ctnode --help + silent-ctnode [Options] -n NAME -s SECRET FILE [FILE ...] + +Options: + + -h, --help: Output usage message and exit + -v, --verbosity Leveled logging output (default: NOTICE) + + -n, --name: Name of the node generating the submission + -s, --secret: Shared secret between the node and its monitor + -o, --output: File to write submission to (default: stdout) + +Each trailing FILE argument must contain a single certificate chain. +` + +type config struct { + // Options + verbosity string + name string + secret string + output string + + // Extracted + log logger.Logger + files []string +} + +func configure(cmd string, args []string) (cfg config, err error) { + fs := flag.NewFlagSet(cmd, flag.ContinueOnError) + fs.Usage = func() {} + flagopt.StringOpt(fs, &cfg.verbosity, "verbosity", "v", logger.LevelNotice.String()) + flagopt.StringOpt(fs, &cfg.name, "name", "n", "") + flagopt.StringOpt(fs, &cfg.secret, "secret", "s", "") + flagopt.StringOpt(fs, &cfg.output, "output", "o", "") + if err = fs.Parse(args); err != nil { + return cfg, err + } + + // Options + lv, err := logger.NewLevel(cfg.verbosity) + if err != nil { + return cfg, fmt.Errorf("invalid verbosity: %v", err) + } + if cfg.name == "" { + return cfg, fmt.Errorf("node name is required") + } + if cfg.secret == "" { + return cfg, fmt.Errorf("node secret is required") + } + cfg.log = logger.New(logger.Config{Level: lv, File: os.Stderr}) + + // Arguments + cfg.files = fs.Args() + if len(cfg.files) == 0 { + return cfg, fmt.Errorf("at least one certificate chain file is required") + } + + return cfg, err +} + +func main() { + cfg, err := configure(os.Args[0], os.Args[1:]) + if err != nil { + if errors.Is(err, flag.ErrHelp) { + fmt.Fprintf(os.Stderr, "%s", usage[1:]) + os.Exit(0) + } + if !strings.Contains(err.Error(), "flag provided but not defined") { + fmt.Fprintf(os.Stderr, "%v\n", err) + } + os.Exit(1) + } + + var chains [][]byte + for i, path := range cfg.files { + b, err := ioutil.ReadData(path) + if err != nil { + cfg.log.Dief("file %d: %v\n", i, err) + } + if _, err := crtutil.CertificateChainFromPEM(b); err != nil { + cfg.log.Dief("file %d: %v\n", i, err) + } + + chains = append(chains, b) + } + + node, err := policy.NewNode(cfg.name, cfg.secret, "http://www.example.org/unused", nil) + if err != nil { + cfg.log.Dief("api: %v\n", err) + } + s, err := submission.New(node, chains) + if err != nil { + cfg.log.Dief("api: %v\n", err) + } + + fp := os.Stdout + if cfg.output != "" { + if fp, err = os.OpenFile(cfg.output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { + cfg.log.Dief("output: %v\n", err) + } + } + + fmt.Fprintf(fp, "%s", string(s)) +} -- cgit v1.2.3