From d983dbae543f93bf1c0019af1f24c11d4a1840ff Mon Sep 17 00:00:00 2001 From: Gibheer Date: Sat, 21 Mar 2015 18:50:55 +0100 Subject: rework the parameter management With this change it is now possible to provide help messages for all commands. This will help to further cleanup and minimize the code base. --- flags.go | 349 ++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 199 insertions(+), 150 deletions(-) (limited to 'flags.go') diff --git a/flags.go b/flags.go index d70120b..3b65d52 100644 --- a/flags.go +++ b/flags.go @@ -7,7 +7,6 @@ import ( "crypto/elliptic" "encoding/base64" "encoding/pem" - "flag" "fmt" "io" "io/ioutil" @@ -62,131 +61,186 @@ type ( signature string // a base64 encoded signature } - // a container for the refined flags - flagSet struct { - // loaded private key - PrivateKey pki.PrivateKey - // loaded public key - PublicKey pki.PublicKey - // the IO handler for input - Input io.ReadCloser - // the IO handler for output - Output io.WriteCloser - // signature from the args - Signature []byte - // private key specific stuff - PrivateKeyGenerationFlags privateKeyGenerationFlags - // a certificate filled with the parameters - CertificateData *pki.CertificateData - // the certificate sign request - CertificateSignRequest *pki.CertificateRequest - } - privateKeyGenerationFlags struct { Type string // type of the private key (rsa, ecdsa) Curve elliptic.Curve // curve for ecdsa Size int // bitsize for rsa } - Flags struct { - Name string // name of the sub function - flagset *flag.FlagSet // the flagset reference for printing the help - flag_container *paramContainer - Flags *flagSet // the end result of the flag setting + flagCheck func()(error) +) - check_list []flagCheck // list of all checks +var ( + CmdRoot = &Command { + Short: "A tool to manage keys and certificates.", + Long: `This tool provides a way to manage private and public keys, create +certificate requests and certificates and sign/verify messages.`, } - flagCheck func()(error) -) + CmdCreatePrivateKey = &Command { + Use: "create-private", + Short: "create a private key", + Long: "Create an ecdsa or rsa key with this command", + Example: "create-private -type=ecdsa -length=521", + Run: create_private_key, + } + + CmdCreatePublicKey = &Command { + Use: "create-public", + Short: "create a public key from a private key", + Long: "Create a public key derived from a private key.", + Example: "create-public -private-key=foo.ecdsa", + Run: create_public_key, + } + + CmdSignInput = &Command { + Use: "sign-input", + Short: "sign a text using a private key", + Long: "Create a signature using a private key", + Example: "sign-input -private-key=foo.ecdsa -input=textfile", + Run: sign_input, + } -// create a new flag handler with the name of the subfunction -func NewFlags(method_name string) *Flags { - flagset := flag.NewFlagSet(method_name, flag.ExitOnError) - flags := &Flags{ - Name: method_name, - Flags: &flagSet{}, - flagset: flagset, - check_list: make([]flagCheck, 0), - flag_container: ¶mContainer{}, + CmdVerifyInput = &Command { + Use: "verify-input", + Short: "verify a text using a signature", + Long: "Verify a text using a signature and a public key.", + Example: "verify-input -public-key=foo.ecdsa.pub -input=textfile -signature=abc456", + Run: verify_input, } - flagset.Usage = flags.Usage - return flags + + CmdCreateSignRequest = &Command { + Use: "create-sign-request", + Short: "create a certificate sign request", + Long: "Create a certificate sign request.", + Example: "create-sign-request -private-key=foo.ecdsa -common-name=foo -serial=1", + Run: create_sign_request, + } + + CmdCreateCert = &Command { + Use: "create-cert", + Short: "create a certificate from a sign request", + Long: "Create a certificate based on a certificate sign request.", + Example: "create-cert -private-key=foo.ecdsa -csr-path=foo.csr", + Run: create_cert, + } + + // variable to hold the raw arguments before checking + flagContainer *paramContainer + + // loaded private key + FlagPrivateKey pki.PrivateKey + // loaded public key + FlagPublicKey pki.PublicKey + // the IO handler for input + FlagInput io.ReadCloser + // the IO handler for output + FlagOutput io.WriteCloser + // signature from the args + FlagSignature []byte + // private key specific stuff + FlagPrivateKeyGeneration privateKeyGenerationFlags + // a certificate filled with the parameters + FlagCertificateData *pki.CertificateData + // the certificate sign request + FlagCertificateSignRequest *pki.CertificateRequest +) + +func InitFlags() { + flagContainer = ¶mContainer{} + CmdRoot.AddCommand( + CmdCreatePrivateKey, + CmdCreatePublicKey, + CmdSignInput, + CmdVerifyInput, + CmdCreateSignRequest, + CmdCreateCert, + ) + + // private-key + InitFlagOutput(CmdCreatePrivateKey) + InitFlagPrivateKeyGeneration(CmdCreatePrivateKey) + // public-key + InitFlagOutput(CmdCreatePublicKey) + InitFlagPrivateKey(CmdCreatePublicKey) + // sign-input + InitFlagInput(CmdSignInput) + InitFlagPrivateKey(CmdSignInput) + InitFlagOutput(CmdSignInput) + // verify-input + InitFlagInput(CmdVerifyInput) + InitFlagPrivateKey(CmdVerifyInput) + InitFlagOutput(CmdVerifyInput) + InitFlagSignature(CmdVerifyInput) + // create-sign-request + InitFlagPrivateKey(CmdCreateSignRequest) + InitFlagOutput(CmdCreateSignRequest) + InitFlagCertificateFields(CmdCreateSignRequest) + // create-certificate + InitFlagPrivateKey(CmdCreateCert) + InitFlagOutput(CmdCreateCert) + InitFlagCSR(CmdCreateCert) } -// check all parameters for validity -func (f *Flags) Parse(options []string) error { - f.flagset.Parse(options) - for _, check := range f.check_list { - // TODO handle error in a betetr way (output specific help, not command help) +func checkFlags(checks... flagCheck) error { + for _, check := range checks { if err := check(); err != nil { - f.Usagef("%s", err) return err } } return nil } -// print a message with the usage part -func (f *Flags) Usagef(message string, args ...interface{}) { - fmt.Fprintf(os.Stderr, "error: " + message + "\n", args...) - f.Usage() -} - -// print the usage of the current flag set -func (f *Flags) Usage() { - fmt.Fprintf(os.Stderr, "usage: %s %s [options]\n", os.Args[0], f.Name) - fmt.Fprint(os.Stderr, "where options are:\n") - f.flagset.PrintDefaults() -} +//// print a message with the usage part +//func (f *Flags) Usagef(message string, args ...interface{}) { +// fmt.Fprintf(os.Stderr, "error: " + message + "\n", args...) +// f.flagset.Flags().Usage() +//} // add the private key option to the requested flags -func (f *Flags) AddPrivateKey() { - f.check_list = append(f.check_list, f.parsePrivateKey) - f.flagset.StringVar(&f.flag_container.privateKeyPath, "private-key", "", "path to the private key") +func InitFlagPrivateKey(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.privateKeyPath, "private-key", "", "path to the private key (required)") } // check the private key flag and load the private key -func (f *Flags) parsePrivateKey() error { - if f.flag_container.privateKeyPath == "" { return fmt.Errorf("No private key given!") } +func checkPrivateKey() error { + if flagContainer.privateKeyPath == "" { return fmt.Errorf("No private key given!") } // check permissions of private key file - info, err := os.Stat(f.flag_container.privateKeyPath) + info, err := os.Stat(flagContainer.privateKeyPath) if err != nil { return fmt.Errorf("Error reading private key: %s", err) } if info.Mode().Perm().String()[4:] != "------" { return fmt.Errorf("private key file modifyable by others!") } - pk, err := ReadPrivateKeyFile(f.flag_container.privateKeyPath) + pk, err := ReadPrivateKeyFile(flagContainer.privateKeyPath) if err != nil { return fmt.Errorf("Error reading private key: %s", err) } - f.Flags.PrivateKey = pk + FlagPrivateKey = pk return nil } // add the public key flag -func (f *Flags) AddPublicKey() { - f.check_list = append(f.check_list, f.parsePublicKey) - f.flagset.StringVar(&f.flag_container.publicKeyPath, "public-key", "", "path to the public key") +func InitFlagPublicKey(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.publicKeyPath, "public-key", "", "path to the public key (required)") } // parse public key flag -func (f *Flags) parsePublicKey() error { - if f.flag_container.publicKeyPath == "" { return fmt.Errorf("No public key given!") } +func checkPublicKey() error { + if flagContainer.publicKeyPath == "" { return fmt.Errorf("No public key given!") } - pu, err := ReadPublicKeyFile(f.flag_container.publicKeyPath) + pu, err := ReadPublicKeyFile(flagContainer.publicKeyPath) if err != nil { return fmt.Errorf("Error reading public key: %s", err) } - f.Flags.PublicKey = pu + FlagPublicKey = pu return nil } // add flag to load certificate sign request -func (f *Flags) AddCSR() { - f.check_list = append(f.check_list, f.parseCSR) - f.flagset.StringVar(&f.flag_container.signRequestPath, "csr-path", "", "path to the certificate sign request") +func InitFlagCSR(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.signRequestPath, "csr-path", "", "path to the certificate sign request") } // parse the certificate sign request -func (f *Flags) parseCSR() error { - rest, err := ioutil.ReadFile(f.flag_container.signRequestPath) +func checkCSR() error { + rest, err := ioutil.ReadFile(flagContainer.signRequestPath) if err != nil { return fmt.Errorf("Error reading certificate sign request: %s", err) } var csr_asn1 []byte @@ -201,31 +255,29 @@ func (f *Flags) parseCSR() error { if len(csr_asn1) == 0 { return fmt.Errorf( "No certificate sign request found in %s", - f.flag_container.signRequestPath, + flagContainer.signRequestPath, ) } csr, err := pki.LoadCertificateSignRequest(csr_asn1) if err != nil { return fmt.Errorf("Invalid certificate sign request: %s", err) } - f.Flags.CertificateSignRequest = csr + FlagCertificateSignRequest = csr return nil } -// add the output parameter to the checklist -func (f *Flags) AddOutput() { - f.check_list = append(f.check_list, f.parseOutput) - f.flagset.StringVar(&f.flag_container.outputPath, "output", "STDOUT", "path to the output or STDOUT") +func InitFlagOutput(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.outputPath, "output", "STDOUT", "path to the output or STDOUT") } // parse the output parameter and open the file handle -func (f *Flags) parseOutput() error { - if f.flag_container.outputPath == "STDOUT" { - f.Flags.Output = os.Stdout +func checkOutput() error { + if flagContainer.outputPath == "STDOUT" { + FlagOutput = os.Stdout return nil } var err error - f.Flags.Output, err = os.OpenFile( - f.flag_container.outputPath, + FlagOutput, err = os.OpenFile( + flagContainer.outputPath, os.O_WRONLY | os.O_APPEND | os.O_CREATE, // do not kill users files! 0600, ) @@ -234,50 +286,49 @@ func (f *Flags) parseOutput() error { } // add the input parameter to load resources from -func (f *Flags) AddInput() { - f.check_list = append(f.check_list, f.parseInput) - f.flagset.StringVar(&f.flag_container.inputPath, "input", "STDIN", "path to the input or STDIN") +func InitFlagInput(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.inputPath, "input", "STDIN", "path to the input or STDIN") } // parse the input parameter and open the file handle -func (f *Flags) parseInput() error { - if f.flag_container.inputPath == "STDIN" { - f.Flags.Input = os.Stdin +func checkInput() error { + if flagContainer.inputPath == "STDIN" { + FlagInput = os.Stdin return nil } var err error - f.Flags.Input, err = os.Open(f.flag_container.inputPath) + FlagInput, err = os.Open(flagContainer.inputPath) if err != nil { return err } return nil } // This function adds the private key generation flags. -func (f *Flags) AddPrivateKeyGenerationFlags() { - f.check_list = append(f.check_list, f.parsePrivateKeyGenerationFlags) - f.flagset.StringVar(&f.flag_container.cryptType, "type", "ecdsa", "the type of the private key (ecdsa, rsa)") - f.flagset.IntVar( - &f.flag_container.length, +func InitFlagPrivateKeyGeneration(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.cryptType, "type", "ecdsa", "the type of the private key (ecdsa, rsa)") + cmd.Flags().IntVar( + &flagContainer.length, "length", 521, - fmt.Sprintf("%d - %d for rsa; %v for ecdsa", RsaLowerLength, RsaUpperLength, EcdsaCurves), + fmt.Sprintf("%d - %d for rsa; one of %v for ecdsa", RsaLowerLength, RsaUpperLength, EcdsaCurves), ) } -func (f *Flags) parsePrivateKeyGenerationFlags() error { - pk_type := f.flag_container.cryptType - f.Flags.PrivateKeyGenerationFlags.Type = pk_type +// check the private key generation variables and move them to the work space +func checkPrivateKeyGeneration() error { + pk_type := flagContainer.cryptType + FlagPrivateKeyGeneration.Type = pk_type switch pk_type { case "ecdsa": - switch f.flag_container.length { - case 224: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P224() - case 256: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P256() - case 384: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P384() - case 521: f.Flags.PrivateKeyGenerationFlags.Curve = elliptic.P521() - default: return fmt.Errorf("Curve %d unknown!", f.flag_container.length) + switch flagContainer.length { + case 224: FlagPrivateKeyGeneration.Curve = elliptic.P224() + case 256: FlagPrivateKeyGeneration.Curve = elliptic.P256() + case 384: FlagPrivateKeyGeneration.Curve = elliptic.P384() + case 521: FlagPrivateKeyGeneration.Curve = elliptic.P521() + default: return fmt.Errorf("Curve %d unknown!", flagContainer.length) } case "rsa": - size := f.flag_container.length + size := flagContainer.length if RsaLowerLength <= size && size <= RsaUpperLength { - f.Flags.PrivateKeyGenerationFlags.Size = size + FlagPrivateKeyGeneration.Size = size } else { return fmt.Errorf("Length of %d is not allowed for rsa!", size) } @@ -287,66 +338,64 @@ func (f *Flags) parsePrivateKeyGenerationFlags() error { } // add the signature flag to load a signature from a signing process -func (f *Flags) AddSignature() { - f.check_list = append(f.check_list, f.parseSignature) - f.flagset.StringVar(&f.flag_container.signature, "signature", "", "the base64 encoded signature to use for verification") +func InitFlagSignature(cmd *Command) { + cmd.Flags().StringVar(&flagContainer.signature, "signature", "", "the base64 encoded signature to use for verification") } // parse the signature flag -func (f *Flags) parseSignature() error { +func checkSignature() error { var err error - f.Flags.Signature, err = base64.StdEncoding.DecodeString(f.flag_container.signature) + FlagSignature, err = base64.StdEncoding.DecodeString(flagContainer.signature) if err != nil { return err } return nil } // add the certificate fields to the flags -func (f *Flags) AddCertificateFields() { - f.check_list = append(f.check_list, f.parseCertificateFields) - f.flagset.StringVar( - &f.flag_container.certificateFlags.manual.serialNumber, +func InitFlagCertificateFields(cmd *Command) { + cmd.Flags().StringVar( + &flagContainer.certificateFlags.manual.serialNumber, "serial", "1", "unique serial number of the CA"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.manual.commonName, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.manual.commonName, "common-name", "", "common name of the entity to certify"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.manual.dnsNames, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.manual.dnsNames, "dns-names", "", "comma separated list of alternative fqdn entries for the entity"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.manual.emailAddresses, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.manual.emailAddresses, "email-address", "", "comma separated list of alternative email entries for the entity"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.manual.ipAddresses, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.manual.ipAddresses, "ip-address", "", "comma separated list of alternative ip entries for the entity"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.Country, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.Country, "country", "", "comma separated list of countries the entitiy resides in"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.Organization, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.Organization, "organization", "", "comma separated list of organizations the entity belongs to"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.OrganizationalUnit, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.OrganizationalUnit, "organization-unit", "", "comma separated list of organization units or departments the entity belongs to"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.Locality, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.Locality, "locality", "", "comma separated list of localities or cities the entity resides in"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.Province, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.Province, "province", "", "comma separated list of provinces the entity resides in"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.StreetAddress, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.StreetAddress, "street-address", "", "comma separated list of street addresses the entity resides in"); - f.flagset.StringVar( - &f.flag_container.certificateFlags.automatic.PostalCode, + cmd.Flags().StringVar( + &flagContainer.certificateFlags.automatic.PostalCode, "postal-code", "", "comma separated list of postal codes of the localities"); } // parse the certificate fields into a raw certificate -func (f *Flags) parseCertificateFields() error { - f.Flags.CertificateData = pki.NewCertificateData() +func checkCertificateFields() error { + FlagCertificateData = pki.NewCertificateData() // convert the automatic flags - container_type := reflect.ValueOf(&f.flag_container.certificateFlags.automatic).Elem() - cert_data_type := reflect.ValueOf(&f.Flags.CertificateData.Subject).Elem() + container_type := reflect.ValueOf(&flagContainer.certificateFlags.automatic).Elem() + cert_data_type := reflect.ValueOf(&FlagCertificateData.Subject).Elem() for _, field := range []string{"Country", "Organization", "OrganizationalUnit", "Locality", "Province", "StreetAddress", "PostalCode"} { @@ -357,8 +406,8 @@ func (f *Flags) parseCertificateFields() error { } // convert the manual flags - data := f.Flags.CertificateData - raw_data := f.flag_container.certificateFlags.manual + data := FlagCertificateData + raw_data := flagContainer.certificateFlags.manual data.Subject.SerialNumber = raw_data.serialNumber data.Subject.CommonName = raw_data.commonName if raw_data.dnsNames != "" { -- cgit v1.2.3-70-g09d2