This repository has been archived by the owner on Jan 24, 2020. It is now read-only.
forked from smallstep/cli
-
Notifications
You must be signed in to change notification settings - Fork 1
/
generate.go
134 lines (123 loc) 路 3.06 KB
/
generate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package otp
import (
"bytes"
"fmt"
"image/png"
"strings"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"github.com/smallstep/cli/command"
"github.com/smallstep/cli/errs"
"github.com/smallstep/cli/flags"
"github.com/smallstep/cli/utils"
"github.com/urfave/cli"
)
func generateCommand() cli.Command {
return cli.Command{
Name: "generate",
Action: command.ActionFunc(generateAction),
Usage: "generate a one-time password",
UsageText: `**step crypto otp generate**`,
Description: `**step crypto otp generate** does TOTP and HTOP`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "issuer, iss",
Usage: `Name of the issuing organization (e.g., smallstep.com)`,
},
cli.StringFlag{
Name: "account",
Usage: `Name of the user's account (e.g., a username or email
address)`,
},
cli.IntFlag{
Name: "period",
Usage: `Number of seconds a TOTP hash is valid. Defaults to 30
seconds.`,
Value: 30,
},
cli.IntFlag{
Name: "length, digits",
Usage: `Length of one-time passwords. Defaults to 6.`,
Value: 6,
},
cli.IntFlag{
Name: "secret-size",
Usage: `Size of generated TOTP secret. Defaults to 20.`,
Value: 20,
},
cli.StringFlag{
Name: "alg, algorithm",
Usage: `Algorithm to use for HMAC. Defaults to SHA1. Must be
one of: SHA1, SHA256, SHA512`,
Value: "SHA1",
},
cli.BoolFlag{
Name: "url",
Usage: `Output a TOTP Key URI. See
https://github.com/google/google-authenticator/wiki/Key-Uri-Format`,
},
cli.StringFlag{
Name: "qr",
Usage: `Write a QR code to the specified path`,
},
flags.Force,
},
}
}
func generateAction(ctx *cli.Context) error {
switch {
case len(ctx.String("issuer")) == 0:
return errs.RequiredFlag(ctx, "issuer")
case len(ctx.String("account")) == 0:
return errs.RequiredFlag(ctx, "account")
}
key, err := generate(ctx)
if err != nil {
return err
}
if ctx.IsSet("qr") {
filename := ctx.String("qr")
// Convert TOTP key into a PNG
var buf bytes.Buffer
img, err := key.Image(200, 200)
if err != nil {
return err
}
png.Encode(&buf, img)
if err := utils.WriteFile(filename, buf.Bytes(), 0644); err != nil {
return errs.FileError(err, filename)
}
}
if ctx.Bool("url") {
fmt.Println(key.String())
} else {
fmt.Println(key.Secret())
}
return nil
}
func algFromString(ctx *cli.Context, alg string) (otp.Algorithm, error) {
switch strings.ToUpper(alg) {
case "SHA1":
return otp.AlgorithmSHA1, nil
case "SHA256":
return otp.AlgorithmSHA256, nil
case "SHA512":
return otp.AlgorithmSHA512, nil
default:
return 0, errs.InvalidFlagValue(ctx, "alg", alg, "SHA1, SHA256, or SHA512")
}
}
func generate(ctx *cli.Context) (*otp.Key, error) {
alg, err := algFromString(ctx, ctx.String("alg"))
if err != nil {
return nil, err
}
return totp.Generate(totp.GenerateOpts{
Issuer: ctx.String("issuer"),
AccountName: ctx.String("account"),
Period: uint(ctx.Int("period")),
SecretSize: uint(ctx.Int("secret-size")),
Digits: otp.Digits(ctx.Int("length")),
Algorithm: alg,
})
}