diff options
| author | Rasmus Dahlberg <rasmus@rgdd.se> | 2023-12-31 09:39:25 +0100 | 
|---|---|---|
| committer | Rasmus Dahlberg <rasmus@rgdd.se> | 2024-01-07 20:22:23 +0100 | 
| commit | e18d36ebae30536c77c61cd5da123991e0ca1629 (patch) | |
| tree | bf4880c0019a6009ab1b671e23ef4a1a4a5e8e08 /cmd/silent-ctnode | |
| parent | 54d980afcbd6f0011d6a162e0003587d26a3e311 (diff) | |
Add drafty prototype
Diffstat (limited to 'cmd/silent-ctnode')
| -rw-r--r-- | cmd/silent-ctnode/main.go | 127 | 
1 files changed, 127 insertions, 0 deletions
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)) +}  | 
