diff options
| -rw-r--r-- | cli.go | 141 | ||||
| -rw-r--r-- | cli_test.go | 99 | ||||
| -rw-r--r-- | cmd/gotp/main.go | 120 | ||||
| -rw-r--r-- | config.go | 53 | ||||
| -rw-r--r-- | config_test.go | 34 | ||||
| -rw-r--r-- | gotp.go | 22 | ||||
| -rw-r--r-- | issuer.go | 57 | ||||
| -rw-r--r-- | provider_aegis.go | 47 |
8 files changed, 340 insertions, 233 deletions
@@ -28,39 +28,42 @@ type Cli struct { func NewCli() (cli *Cli, err error) { var ( - logp = "NewCli" + logp = `NewCli` + + userConfigDir string + cfgFile string ) cli = &Cli{} - userConfigDir, err := os.UserConfigDir() + userConfigDir, err = os.UserConfigDir() if err != nil { - return nil, fmt.Errorf("%s: UserConfigDir: %w", logp, err) + return nil, fmt.Errorf(`%s: UserConfigDir: %w`, logp, err) } - cfgFile := filepath.Join(userConfigDir, configDir, configFile) + cfgFile = filepath.Join(userConfigDir, configDir, configFile) cli.cfg, err = newConfig(cfgFile) if err != nil { - return nil, fmt.Errorf("%s: UserConfigDir: %w", logp, err) + return nil, fmt.Errorf(`%s: UserConfigDir: %w`, logp, err) } if cli.cfg.isNotExist { cli.cfg.PrivateKey, err = cli.inputPrivateKey(os.Stdin) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } } if len(cli.cfg.PrivateKey) > 0 { cli.cfg.privateKey, err = cli.loadPrivateKey(cli.cfg.PrivateKey, nil) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } } if cli.cfg.isNotExist { err = cli.cfg.save() if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } } @@ -68,17 +71,15 @@ func NewCli() (cli *Cli, err error) { } func (cli *Cli) inputPrivateKey(stdin *os.File) (privateKeyFile string, err error) { - fmt.Printf("Seems like this is your first time using this gotp.\n") - fmt.Printf("If you would like to encrypt the secret, please\n") - fmt.Printf("enter the path to private key or enter to skip it: ") + fmt.Println(`Seems like this is your first time using this gotp.`) + fmt.Println(`If you would like to encrypt the secret, please`) + fmt.Printf(`enter the path to private key or enter to skip it: `) fmt.Fscanln(stdin, &privateKeyFile) return privateKeyFile, nil } -// // loadPrivateKey parse the RSA private key with optional passphrase. -// func (cli *Cli) loadPrivateKey(privateKeyFile string, pass []byte) ( rsaPrivateKey *rsa.PrivateKey, err error, ) { @@ -87,14 +88,18 @@ func (cli *Cli) loadPrivateKey(privateKeyFile string, pass []byte) ( } var ( - logp = "loadPrivateKey" + logp = `loadPrivateKey` + errPassMissing = &ssh.PassphraseMissingError{} + privateKey interface{} + stdin int + rawPem []byte ok bool ) - rawPem, err := os.ReadFile(privateKeyFile) + rawPem, err = os.ReadFile(privateKeyFile) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } if len(pass) == 0 { @@ -103,48 +108,48 @@ func (cli *Cli) loadPrivateKey(privateKeyFile string, pass []byte) ( privateKey, err = ssh.ParseRawPrivateKeyWithPassphrase(rawPem, pass) } if err != nil { - errPassMissing := &ssh.PassphraseMissingError{} if !errors.As(err, &errPassMissing) { - return nil, fmt.Errorf("%s %q: %w", logp, privateKeyFile, err) + return nil, fmt.Errorf(`%s %q: %w`, logp, privateKeyFile, err) } - fmt.Printf("Enter passphrase for %s: ", privateKeyFile) + fmt.Printf(`Enter passphrase for %s: `, privateKeyFile) + + stdin = int(os.Stdin.Fd()) - stdin := os.Stdin.Fd() - pass, err := terminal.ReadPassword(int(stdin)) - fmt.Printf("\n") + pass, err = terminal.ReadPassword(stdin) + fmt.Println() if err != nil { - return nil, fmt.Errorf("%s %q: %w", logp, privateKeyFile, err) + return nil, fmt.Errorf(`%s %q: %w`, logp, privateKeyFile, err) } return cli.loadPrivateKey(privateKeyFile, pass) } rsaPrivateKey, ok = privateKey.(*rsa.PrivateKey) if !ok { - return nil, fmt.Errorf("%s: invalid or unsupported private key", logp) + return nil, fmt.Errorf(`%s: invalid or unsupported private key`, logp) } return rsaPrivateKey, nil } -// // Add new issuer to the config. -// func (cli *Cli) Add(issuer *Issuer) (err error) { if issuer == nil { return nil } - logp := "Add" + var ( + logp = `Add` + ) err = cli.add(issuer) if err != nil { - return fmt.Errorf("%s: %w", logp, err) + return fmt.Errorf(`%s: %w`, logp, err) } err = cli.cfg.save() if err != nil { - return fmt.Errorf("%s: %w", logp, err) + return fmt.Errorf(`%s: %w`, logp, err) } return nil @@ -157,16 +162,19 @@ func (cli *Cli) Generate(label string, n int) (listOtp []string, err error) { b32Enc = base32.StdEncoding.WithPadding(base32.NoPadding) cryptoHash totp.CryptoHash + issuer *Issuer + secret []byte + proto totp.Protocol ) - issuer, err := cli.cfg.get(label) + issuer, err = cli.cfg.get(label) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } - secret, err := b32Enc.DecodeString(issuer.Secret) + secret, err = b32Enc.DecodeString(issuer.Secret) if err != nil { - return nil, fmt.Errorf("%s: secret is not a valid base32 encoding: %w", logp, err) + return nil, fmt.Errorf(`%s: secret is not a valid base32 encoding: %w`, logp, err) } switch issuer.Hash { @@ -178,105 +186,116 @@ func (cli *Cli) Generate(label string, n int) (listOtp []string, err error) { cryptoHash = totp.CryptoHashSHA1 } - proto := totp.New(cryptoHash, issuer.Digits, issuer.TimeStep) + proto = totp.New(cryptoHash, issuer.Digits, issuer.TimeStep) listOtp, err = proto.GenerateN(secret, n) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } return listOtp, nil } -// // Import the TOTP configuration from file format based on provider. -// func (cli *Cli) Import(providerName, file string) (n int, err error) { - logp := "Import" + var ( + logp = `Import` + + issuers []*Issuer + issuer *Issuer + ) providerName = strings.ToLower(providerName) switch providerName { case providerNameAegis: default: - return 0, fmt.Errorf("%s: unknown provider %q", logp, providerName) + return 0, fmt.Errorf(`%s: unknown provider %q`, logp, providerName) } - issuers, err := parseProviderAegis(file) + issuers, err = parseProviderAegis(file) if err != nil { - return 0, fmt.Errorf("%s: %w", logp, err) + return 0, fmt.Errorf(`%s: %w`, logp, err) } - for _, issuer := range issuers { + for _, issuer = range issuers { err = issuer.validate() if err != nil { - return 0, fmt.Errorf("%s: %w", logp, err) + return 0, fmt.Errorf(`%s: %w`, logp, err) } err = cli.cfg.add(issuer) if err != nil { - return 0, fmt.Errorf("%s: %w", logp, err) + return 0, fmt.Errorf(`%s: %w`, logp, err) } } err = cli.cfg.save() if err != nil { - return 0, fmt.Errorf("%s: %w", logp, err) + return 0, fmt.Errorf(`%s: %w`, logp, err) } return len(issuers), nil } -// // List all labels sorted in ascending order. -// func (cli *Cli) List() (labels []string) { - for label := range cli.cfg.Issuers { + var ( + label string + ) + + for label = range cli.cfg.Issuers { labels = append(labels, label) } sort.Strings(labels) return labels } -// // Remove a TOTP configuration by its label. -// func (cli *Cli) Remove(label string) (err error) { - logp := "Remove" + var ( + logp = `Remove` + + ok bool + ) label = strings.ToLower(label) - _, ok := cli.cfg.Issuers[label] + + _, ok = cli.cfg.Issuers[label] if !ok { - return fmt.Errorf("%s: %q not exist", logp, label) + return fmt.Errorf(`%s: %q not exist`, logp, label) } delete(cli.cfg.Issuers, label) err = cli.cfg.save() if err != nil { - return fmt.Errorf("%s: %w", logp, err) + return fmt.Errorf(`%s: %w`, logp, err) } return nil } -// // Rename a label to newLabel. // It will return an error if the label parameter is not exist or newLabel // already exist. -// func (cli *Cli) Rename(label, newLabel string) (err error) { - logp := "Rename" + var ( + logp = `Rename` + + rawValue string + ok bool + ) label = strings.ToLower(label) - rawValue, ok := cli.cfg.Issuers[label] + rawValue, ok = cli.cfg.Issuers[label] if !ok { - return fmt.Errorf("%s: %q not exist", logp, label) + return fmt.Errorf(`%s: %q not exist`, logp, label) } newLabel = strings.ToLower(newLabel) _, ok = cli.cfg.Issuers[newLabel] if ok { - return fmt.Errorf("%s: new label %q already exist", logp, newLabel) + return fmt.Errorf(`%s: new label %q already exist`, logp, newLabel) } delete(cli.cfg.Issuers, label) @@ -285,7 +304,7 @@ func (cli *Cli) Rename(label, newLabel string) (err error) { err = cli.cfg.save() if err != nil { - return fmt.Errorf("%s: %w", logp, err) + return fmt.Errorf(`%s: %w`, logp, err) } return nil diff --git a/cli_test.go b/cli_test.go index 9827def..8bd1c6a 100644 --- a/cli_test.go +++ b/cli_test.go @@ -12,34 +12,44 @@ import ( ) func TestCli_inputPrivateKey(t *testing.T) { - cli := &Cli{ - cfg: &config{ - file: "testdata/save.conf", - isNotExist: true, - }, - } - - cases := []struct { + type testCase struct { desc string privateKey string exp string - }{{ - desc: "Without private key", + } + + var ( + cli = &Cli{ + cfg: &config{ + file: `testdata/save.conf`, + isNotExist: true, + }, + } + + c testCase + r *os.File + w *os.File + gotPrivateKeyFile string + err error + ) + + var cases = []testCase{{ + desc: `Without private key`, exp: "[gotp]\nprivate_key =\n", }, { - desc: "With private key", - privateKey: "testdata/rsa", + desc: `With private key`, + privateKey: `testdata/rsa`, }} - for _, c := range cases { - r, w, err := os.Pipe() + for _, c = range cases { + r, w, err = os.Pipe() if err != nil { t.Fatal(err) } fmt.Fprintf(w, "%s\n", c.privateKey) - gotPrivateKeyFile, err := cli.inputPrivateKey(r) + gotPrivateKeyFile, err = cli.inputPrivateKey(r) if err != nil { t.Fatal(err) } @@ -49,59 +59,70 @@ func TestCli_inputPrivateKey(t *testing.T) { } func TestCli_Add(t *testing.T) { - cli := &Cli{ - cfg: &config{ - Issuers: make(map[string]string), - file: "testdata/add.conf", - }, + type testCase struct { + issuer *Issuer + desc string + expError string + expConfig string } - err := cli.cfg.save() + var ( + cli = &Cli{ + cfg: &config{ + Issuers: make(map[string]string), + file: `testdata/add.conf`, + }, + } + + err error + ) + + err = cli.cfg.save() if err != nil { t.Fatal(err) } - cases := []struct { - desc string - issuer *Issuer - expError string - expConfig string - }{{ - desc: "With nil issuer", + var cases = []testCase{{ + desc: `With nil issuer`, expConfig: "[gotp]\nprivate_key =\n", }, { - desc: "With invalid label", + desc: `With invalid label`, issuer: &Issuer{ - Label: "Not@valid", + Label: `Not@valid`, }, expError: `Add: validate: invalid label "Not@valid"`, }, { - desc: "With invalid hash", + desc: `With invalid hash`, issuer: &Issuer{ - Label: "Test", - Hash: "SHA255", + Label: `Test`, + Hash: `SHA255`, }, expError: `Add: validate: invalid algorithm "SHA255"`, }, { - desc: "With valid label", + desc: `With valid label`, issuer: &Issuer{ - Label: "Test", + Label: `Test`, Hash: HashSHA1, - Secret: "x", + Secret: `x`, }, expConfig: "[gotp]\nprivate_key =\n\n[gotp \"issuer\"]\ntest = SHA1:x:6:30:\n", }} - for _, c := range cases { + var ( + c testCase + got []byte + ) + + for _, c = range cases { t.Log(c.desc) err = cli.Add(c.issuer) if err != nil { - test.Assert(t, "error", c.expError, err.Error()) + test.Assert(t, `error`, c.expError, err.Error()) continue } - got, err := os.ReadFile(cli.cfg.file) + got, err = os.ReadFile(cli.cfg.file) if err != nil { t.Fatal(err) } diff --git a/cmd/gotp/main.go b/cmd/gotp/main.go index 7b95cbf..f031e8b 100644 --- a/cmd/gotp/main.go +++ b/cmd/gotp/main.go @@ -15,16 +15,23 @@ import ( ) const ( - cmdName = "gotp" - cmdAdd = "add" - cmdGenerate = "gen" - cmdImport = "import" - cmdList = "list" - cmdRemove = "remove" - cmdRename = "rename" + cmdName = `gotp` + cmdAdd = `add` + cmdGenerate = `gen` + cmdImport = `import` + cmdList = `list` + cmdRemove = `remove` + cmdRename = `rename` ) func main() { + var ( + cmd string + cli *gotp.Cli + err error + args []string + ) + log.SetFlags(0) flag.Usage = func() { @@ -33,49 +40,50 @@ func main() { } flag.Parse() - args := flag.Args() + args = flag.Args() + if len(args) == 0 { flag.Usage() } - cmd := strings.ToLower(args[0]) + cmd = strings.ToLower(args[0]) switch cmd { case cmdAdd: if len(args) < 3 { - log.Printf("%s %s: missing parameters", cmdName, cmd) + log.Printf(`%s %s: missing parameters`, cmdName, cmd) os.Exit(1) } case cmdGenerate: if len(args) < 2 { - log.Printf("%s %s: missing parameters", cmdName, cmd) + log.Printf(`%s %s: missing parameters`, cmdName, cmd) os.Exit(1) } case cmdImport: if len(args) <= 2 { - log.Printf("%s %s: missing parameters", cmdName, cmd) + log.Printf(`%s %s: missing parameters`, cmdName, cmd) os.Exit(1) } case cmdList: // NOOP. case cmdRemove: if len(args) <= 1 { - log.Printf("%s %s: missing parameters", cmdName, cmd) + log.Printf(`%s %s: missing parameters`, cmdName, cmd) os.Exit(1) } case cmdRename: if len(args) <= 2 { - log.Printf("%s %s: missing parameters", cmdName, cmd) + log.Printf(`%s %s: missing parameters`, cmdName, cmd) os.Exit(1) } default: - log.Printf("%s: unknown command %q", cmdName, cmd) + log.Printf(`%s: unknown command %q`, cmdName, cmd) flag.Usage() } - cli, err := gotp.NewCli() + cli, err = gotp.NewCli() if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } @@ -96,40 +104,51 @@ func main() { } func doAdd(cli *gotp.Cli, args []string) { - label := args[1] - rawConfig := args[2] - issuer, err := gotp.NewIssuer(label, rawConfig, nil) + var ( + label = args[1] + rawConfig = args[2] + + issuer *gotp.Issuer + err error + ) + + issuer, err = gotp.NewIssuer(label, rawConfig, nil) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } err = cli.Add(issuer) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } - fmt.Println("OK") + fmt.Println(`OK`) } func doGenerate(cli *gotp.Cli, args []string) { var ( label = args[1] n int = 1 - err error + + listOtp []string + otp string + err error ) + if len(args) >= 3 { n, err = strconv.Atoi(args[2]) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } } - listOtp, err := cli.Generate(label, n) + + listOtp, err = cli.Generate(label, n) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } - for _, otp := range listOtp { + for _, otp = range listOtp { fmt.Println(otp) } } @@ -138,40 +157,57 @@ func doImport(cli *gotp.Cli, args []string) { var ( providerName = args[1] file = args[2] + + n int + err error ) - n, err := cli.Import(providerName, file) + n, err = cli.Import(providerName, file) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } - fmt.Printf("OK - %d imported", n) + fmt.Printf(`OK - %d imported`, n) } func doList(cli *gotp.Cli) { - labels := cli.List() - for _, label := range labels { + var ( + labels []string = cli.List() + + label string + ) + + for _, label = range labels { fmt.Println(label) } } func doRemove(cli *gotp.Cli, args []string) { - label := args[1] - err := cli.Remove(label) + var ( + label = args[1] + + err error + ) + + err = cli.Remove(label) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } - fmt.Println("OK") + fmt.Println(`OK`) } func doRename(cli *gotp.Cli, args []string) { - label := args[1] - newLabel := args[2] + var ( + label = args[1] + newLabel = args[2] + + err error + ) - err := cli.Rename(label, newLabel) + err = cli.Rename(label, newLabel) if err != nil { - log.Printf("%s: %s", cmdName, err) + log.Printf(`%s: %s`, cmdName, err) os.Exit(1) } - fmt.Println("OK") + fmt.Println(`OK`) } @@ -16,46 +16,52 @@ import ( ) const ( - valueSeparator = ":" + valueSeparator = `:` ) type config struct { - PrivateKey string `ini:"gotp::private_key"` + privateKey *rsa.PrivateKey // Only RSA private key can do encryption. + Issuers map[string]string `ini:"gotp:issuer"` + PrivateKey string `ini:"gotp::private_key"` file string isNotExist bool - privateKey *rsa.PrivateKey // Only RSA private key can do encryption. } func newConfig(file string) (cfg *config, err error) { - logp := "newConfig" + var ( + logp = `newConfig` + + in *ini.Ini + dir string + ) cfg = &config{ Issuers: make(map[string]string), file: file, } - in, err := ini.Open(file) + in, err = ini.Open(file) if err != nil { if !errors.Is(err, fs.ErrNotExist) { - return nil, fmt.Errorf("%s: Open %q: %w", logp, file, err) + return nil, fmt.Errorf(`%s: Open %q: %w`, logp, file, err) } cfg.isNotExist = true } if cfg.isNotExist { - dir := filepath.Dir(file) + dir = filepath.Dir(file) err = os.MkdirAll(dir, 0700) if err != nil { - return nil, fmt.Errorf("%s: MkdirAll %q: %w", logp, dir, err) + return nil, fmt.Errorf(`%s: MkdirAll %q: %w`, logp, dir, err) } return cfg, nil } err = in.Unmarshal(cfg) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } return cfg, nil @@ -73,7 +79,7 @@ func (cfg *config) add(issuer *Issuer) (err error) { _, exist = cfg.Issuers[issuer.Label] if exist { - return fmt.Errorf("duplicate issuer name %q", issuer.Label) + return fmt.Errorf(`duplicate issuer name %q`, issuer.Label) } value, err = issuer.pack(cfg.privateKey) @@ -86,22 +92,25 @@ func (cfg *config) add(issuer *Issuer) (err error) { return nil } -// // get the issuer by its name. -// func (cfg *config) get(name string) (issuer *Issuer, err error) { - logp := "get" + var ( + logp = `get` + + v string + ok bool + ) name = strings.ToLower(name) - v, ok := cfg.Issuers[name] + v, ok = cfg.Issuers[name] if !ok { - return nil, fmt.Errorf("%s: issuer %q not found", logp, name) + return nil, fmt.Errorf(`%s: issuer %q not found`, logp, name) } issuer, err = NewIssuer(name, v, cfg.privateKey) if err != nil { - return nil, fmt.Errorf("%s %q: %w", logp, name, err) + return nil, fmt.Errorf(`%s %q: %w`, logp, name, err) } return issuer, nil @@ -109,16 +118,20 @@ func (cfg *config) get(name string) (issuer *Issuer, err error) { // save the config to file. func (cfg *config) save() (err error) { - logp := "save" + var ( + logp = `save` + + b []byte + ) - b, err := ini.Marshal(cfg) + b, err = ini.Marshal(cfg) if err != nil { - return fmt.Errorf("%s %s: %w", logp, cfg.file, err) + return fmt.Errorf(`%s %s: %w`, logp, cfg.file, err) } err = os.WriteFile(cfg.file, b, 0600) if err != nil { - return fmt.Errorf("%s %s: %w", logp, cfg.file, err) + return fmt.Errorf(`%s %s: %w`, logp, cfg.file, err) } return nil diff --git a/config_test.go b/config_test.go index eea7a05..772ea1e 100644 --- a/config_test.go +++ b/config_test.go @@ -10,33 +10,41 @@ import ( ) func TestNewConfig(t *testing.T) { - cases := []struct { + type testCase struct { + expConfig *config desc string configFile string - expConfig *config expError string - }{{ - desc: "With openssh rsa", - configFile: "testdata/rsa.conf", + } + + var cases = []testCase{{ + desc: `With openssh rsa`, + configFile: `testdata/rsa.conf`, expConfig: &config{ - PrivateKey: "testdata/rsa", + PrivateKey: `testdata/rsa`, Issuers: map[string]string{ - "email-domain": "XYZ", - "test": "ABCD", + `email-domain`: `XYZ`, + `test`: `ABCD`, }, - file: "testdata/rsa.conf", + file: `testdata/rsa.conf`, }, }} - for _, c := range cases { + var ( + c testCase + gotConfig *config + err error + ) + + for _, c = range cases { t.Log(c.desc) - gotConfig, err := newConfig(c.configFile) + gotConfig, err = newConfig(c.configFile) if err != nil { - test.Assert(t, "error", c.expError, err.Error()) + test.Assert(t, `error`, c.expError, err.Error()) continue } - test.Assert(t, "Issuer", c.expConfig, gotConfig) + test.Assert(t, `Issuer`, c.expConfig, gotConfig) } } @@ -10,30 +10,30 @@ import ( // List of available algorithm for Provider. const ( - HashSHA1 = "SHA1" // Default algorithm. - HashSHA256 = "SHA256" - HashSHA512 = "SHA512" + HashSHA1 = `SHA1` // Default algorithm. + HashSHA256 = `SHA256` + HashSHA512 = `SHA512` ) const ( - configDir = "gotp" - configFile = "gotp.conf" + configDir = `gotp` + configFile = `gotp.conf` defaultHash = HashSHA1 // List of known providers - providerNameAegis = "aegis" + providerNameAegis = `aegis` ) -// // normalizeLabel convert non alpha number, hyphen, underscore, or period -// characters into "-". -// +// characters into `-`. func normalizeLabel(in string) (out string) { var ( - buf strings.Builder replacement rune = '-' + + buf strings.Builder + r rune ) - for _, r := range in { + for _, r = range in { if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_' || r == '.' { buf.WriteRune(r) @@ -16,44 +16,45 @@ import ( "github.com/shuLhan/share/lib/totp" ) -// // Issuer contains the configuration for single TOTP issuer, including // their unique label, algorithm, secret key, and number of digits. -// type Issuer struct { + Name string Label string Hash string Secret string // The secret value in base32. + raw []byte Digits int TimeStep int - Name string - raw []byte } -// // NewIssuer create and initialize new issuer from raw value. // If the rsaPrivateKey is not nil, that means the rawConfig is encrypted. -// func NewIssuer(label, rawConfig string, rsaPrivateKey *rsa.PrivateKey) (issuer *Issuer, err error) { - logp := "NewIssuer" + var ( + logp = `NewIssuer` + + vals []string + vbytes []byte + ) if rsaPrivateKey != nil { - cipherText, err := base64.StdEncoding.DecodeString(rawConfig) + vbytes, err = base64.StdEncoding.DecodeString(rawConfig) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } - raw, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, rsaPrivateKey, cipherText, nil) + vbytes, err = rsa.DecryptOAEP(sha256.New(), rand.Reader, rsaPrivateKey, vbytes, nil) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } - rawConfig = string(raw) + rawConfig = string(vbytes) } - vals := strings.Split(rawConfig, valueSeparator) + vals = strings.Split(rawConfig, valueSeparator) if len(vals) < 2 { - return nil, fmt.Errorf("%s: invalid value %q", logp, rawConfig) + return nil, fmt.Errorf(`%s: invalid value %q`, logp, rawConfig) } issuer = &Issuer{ Label: label, @@ -63,7 +64,7 @@ func NewIssuer(label, rawConfig string, rsaPrivateKey *rsa.PrivateKey) (issuer * if len(vals) >= 3 { issuer.Digits, err = strconv.Atoi(vals[2]) if err != nil { - return nil, fmt.Errorf("%s: invalid digits %s: %w", logp, vals[2], err) + return nil, fmt.Errorf(`%s: invalid digits %s: %w`, logp, vals[2], err) } } else { issuer.Digits = totp.DefCodeDigits @@ -71,7 +72,7 @@ func NewIssuer(label, rawConfig string, rsaPrivateKey *rsa.PrivateKey) (issuer * if len(vals) >= 4 { issuer.TimeStep, err = strconv.Atoi(vals[3]) if err != nil { - return nil, fmt.Errorf("%s: invalid time step %s: %w", logp, vals[3], err) + return nil, fmt.Errorf(`%s: invalid time step %s: %w`, logp, vals[3], err) } } else { issuer.TimeStep = totp.DefTimeStep @@ -84,19 +85,18 @@ func NewIssuer(label, rawConfig string, rsaPrivateKey *rsa.PrivateKey) (issuer * } func (issuer *Issuer) String() string { - return fmt.Sprintf("%s:%s:%d:%d:%s", issuer.Hash, issuer.Secret, + return fmt.Sprintf(`%s:%s:%d:%d:%s`, issuer.Hash, issuer.Secret, issuer.Digits, issuer.TimeStep, issuer.Name) } -// -// pack the Issuer into string separated by ":". +// pack the Issuer into string separated by `:`. // If the privateKey is not nil, the string will be encrypted and encoded to // base64. -// func (issuer *Issuer) pack(privateKey *rsa.PrivateKey) (value string, err error) { var ( - logp = "pack" + logp = `pack` plainText = issuer.String() + rng = rand.Reader ) issuer.raw = []byte(plainText) @@ -104,10 +104,9 @@ func (issuer *Issuer) pack(privateKey *rsa.PrivateKey) (value string, err error) return string(issuer.raw), nil } - rng := rand.Reader issuer.raw, err = rsa.EncryptOAEP(sha256.New(), rng, &privateKey.PublicKey, issuer.raw, nil) if err != nil { - return "", fmt.Errorf("%s: %w", logp, err) + return ``, fmt.Errorf(`%s: %w`, logp, err) } value = base64.StdEncoding.EncodeToString(issuer.raw) @@ -116,23 +115,25 @@ func (issuer *Issuer) pack(privateKey *rsa.PrivateKey) (value string, err error) } func (issuer *Issuer) validate() (err error) { - logp := "validate" + var ( + logp = `validate` + ) if !ini.IsValidVarName(issuer.Label) { - return fmt.Errorf("%s: invalid label %q", logp, issuer.Label) + return fmt.Errorf(`%s: invalid label %q`, logp, issuer.Label) } issuer.Hash = strings.ToUpper(issuer.Hash) switch issuer.Hash { - case "": + case ``: issuer.Hash = defaultHash case HashSHA1, HashSHA256, HashSHA512: // NOOP default: - return fmt.Errorf("%s: invalid algorithm %q", logp, issuer.Hash) + return fmt.Errorf(`%s: invalid algorithm %q`, logp, issuer.Hash) } if len(issuer.Secret) == 0 { - return fmt.Errorf("%s: empty key", logp) + return fmt.Errorf(`%s: empty key`, logp) } if issuer.Digits <= 0 { issuer.Digits = totp.DefCodeDigits diff --git a/provider_aegis.go b/provider_aegis.go index 8a24c37..bdc71ec 100644 --- a/provider_aegis.go +++ b/provider_aegis.go @@ -12,43 +12,52 @@ import ( ) func parseProviderAegis(file string) (issuers []*Issuer, err error) { - logp := "parseProviderAegis" + var ( + logp = `parseProviderAegis` - b, err := os.ReadFile(file) + u *url.URL + issuer *Issuer + q url.Values + val string + lines [][]byte + line []byte + b []byte + x int + ) + + b, err = os.ReadFile(file) if err != nil { - return nil, fmt.Errorf("%s: %w", logp, err) + return nil, fmt.Errorf(`%s: %w`, logp, err) } - lines := bytes.Split(b, []byte("\n")) - for x, line := range lines { - u, err := url.Parse(string(line)) + lines = bytes.Split(b, []byte("\n")) + for x, line = range lines { + u, err = url.Parse(string(line)) if err != nil { - return nil, fmt.Errorf("%s: line %d: invalid format %q", logp, x, line) + return nil, fmt.Errorf(`%s: line %d: invalid format %q`, logp, x, line) } - if u.Host != "totp" { + if u.Host != `totp` { continue } - q := u.Query() - issuer := &Issuer{ + q = u.Query() + issuer = &Issuer{ Label: normalizeLabel(u.Path[1:]), - Hash: q.Get("algorithm"), - Secret: q.Get("secret"), - Name: q.Get("issuer"), + Hash: q.Get(`algorithm`), + Secret: q.Get(`secret`), + Name: q.Get(`issuer`), } - val := q.Get("digits") + val = q.Get(`digits`) issuer.Digits, err = strconv.Atoi(val) if err != nil { - return nil, fmt.Errorf("%s: line %d: invalid digits %q", - logp, x, val) + return nil, fmt.Errorf(`%s: line %d: invalid digits %q`, logp, x, val) } - val = q.Get("period") + val = q.Get(`period`) issuer.TimeStep, err = strconv.Atoi(val) if err != nil { - return nil, fmt.Errorf("%s: line %d: invalid period %q", - logp, x, val) + return nil, fmt.Errorf(`%s: line %d: invalid period %q`, logp, x, val) } issuers = append(issuers, issuer) |
