Two-step authentication is used in many validations. If on the mobile client, if you use a computer, each time you have to take out the phone, manually entered. And worry about being out of date. Efficiency is not very high.
I am now dealing with the following ways:
- Alfred Workflow Support on Mac
- Chrom Extended Support
- Mobile Client
- CLI Logging Tool
Today mainly introduces the CLI tools, I am now in Golang, looking for some practiced hand projects.
First on the code
Package Mainimport ("FMT" "OS" "Log" "Sort" "github.com/urfave/cli" "Gopkg.in/ini.v1" "Crypto/hmac" "CRYPTO/SHA1" "Strings" "Encoding/base32" "Time" "Github.com/atotto/clipboard" "StrConv") func tobytes (VA Lue Int64) []byte {var result []byte Mask: = Int64 (0xFF) Shifts: = [8]uint16{56, max, 8, 0} fo R _, Shift: = range shifts {result = Append (result, byte ((value>>shift) &mask)} return Result}fun C toUint32 (bytes []byte) UInt32 {return (UInt32 (bytes[0]) <<) + (UInt32 (bytes[1]) << +) + (UINT (Bytes[2]) << 8) + UInt32 (bytes[3])}func Onetimepassword (key []byte, Value []byte] UInt32 {//Sign the value U Sing hmac-sha1 hmacSha1: = HMAC. New (SHA1. New, key) Hmacsha1.write (value) Hash: = Hmacsha1.sum (nil) Offset: = Hash[len (hash)-1] & 0x0F//Get a 32-bit (4-byte) chunk from the hash starting at offset hashparts: = hash[offset:offset+4]//IGnore the most significant bit as per RFC 4226 hashparts[0] = hashparts[0] & 0x7F Number: = ToUint32 (Hashparts) Size to 6 digits//One million are the first number with 7 digits so the remainder//of the Division would ALW Ays return < 7 digits pwd: = number% 1000000 return Pwd}func CLI () {app: = CLI. Newapp () app. Name = "Google authentiator CLI" app. Usage = "Create, List, Delete, Copy your GA" app. Version = "0.0.1" app. Commands = []CLI. Command {Name: "list", Aliases: []string{"L"}, Usage: "list | L ", Description:" List all item ", Category:" Show ", Action:func (c *cli. Context) Error {list () return nil},}, {Name: "Show", Aliases: []string{"S"}, Usage: "Show | S github.com ", Description:" Show All Item ", Category:" Show ", Action:func (c *cli. ConText) Error {item: = C.args (). First () Show (item) return nil},}, {Name: "Copy", Aliases: []string{"C"}, Usage: "Copy | C Github.com ", Usagetext:" Copy ga to Clipboard ", Category:" Show ", Action:func (c *cli.c Ontext) Error {Item: = C.args (). First () copy (item) return nil},}, {Name: "Create", Aliases: []string{"R"}, Usage: "create| R github.com xxxxxxxxxx ", Usagetext:" Create a new GA Secrete item ", Category:" Store ", a Ction:func (c *cli. Context) Error {Item: = C.args (). First () Secret: = C.args () [1] Create (item, secret) return nil}, }, {Name: "Modify", Aliases: []string{"M"}, Usage: "Modify | M gIthub.com xxxxxxxxx ", Usagetext:" Modify one item Secrete ", Category:" Store ", Action:fu NC (c *cli. Context) Error {Item: = C.args (). First () Secret: = C.args () [1] Modify (item, secret) return nil}, }, {Name: "delete", Aliases: []string{"D"}, Usage: "Delete | D github.com ", Usagetext:" Delete one item Secrete ", Category:" Store ", Action:func (c *c Li. Context) Error {Delete (C.args (). First ()) return nil},},} app. Action = func (c *cli. Context) Error {FMT. PRINTLN ("can use List") return nil} sort. Sort (CLI. Flagsbyname (app. Flags)) Cli. Helpflag = CLI. Boolflag {Name: "Help, H", Usage: "help! Help! ","} CLI. Versionflag = CLI. Boolflag {Name: "print-version, V", Usage: "Print Version",} ERR: = App. Run (OS. Args) If err! = Nil {log. Fatal (Err)}}func Create (item, secret String) error {File,er:=os. Open ("/tmp/myga.ini") if er! = Nil {file, _ = os. Create ("/tmp/myga.ini")} file. Close () cfg, err: = ini. Load ("/tmp/myga.ini") if err! = Nil {panic (err)} If _, err: = cfg. GetSection (item); Err = = Nil {fmt. PRINTLN (item + "is Exist!!!!") Os. Exit (1)} cfg. Newsection (item) cfg. Section (item). Key ("secret"). SetValue (Secret) cfg. SaveTo ("/tmp/myga.ini") return Nil}func Show (item string) error {File,er:=os. Open ("/tmp/myga.ini") if Er!=nil && os. Isnotexist (er) {fmt. PRINTLN ("No Data!!!") Os. Exit (1)} file. Close () cfg, err: = ini. Load ("/tmp/myga.ini") if err! = Nil {panic (ERR)} section, Err: = cfg. GetSection (item) If Err! = Nil {panic (err)} key, Err: = section. GetKey ("secret") if err! = Nil {panic (err)} inputnospaces: = strIngs. Replace (key. String (), "", "" ",-1) Inputnospacesupper: = Strings. ToUpper (inputnospaces) KeyS, Erro: = Base32. Stdencoding.decodestring (inputnospacesupper) if erro! = nil {fmt. Fprintln (OS. Stderr, Err. Error ()) OS. Exit (1)}//Generate a one-time password using the time at 30-second intervals epochseconds: = time. Now (). Unix () pwd: = Onetimepassword (KeyS, Tobytes (EPOCHSECONDS/30)) Secondsremaining: =-(epochseconds%) fmt. Printf ("%06d (%d second (s) remaining) \ n", pwd, secondsremaining) return Nil}func copy (item string) error {file,er:= Os. Open ("/tmp/myga.ini") if Er!=nil && os. Isnotexist (er) {fmt. PRINTLN ("No Data!!!") Os. Exit (1)} file. Close () cfg, err: = ini. Load ("/tmp/myga.ini") if err! = Nil {panic (ERR)} section, Err: = cfg. GetSection (item) If Err! = Nil {panic (err)} key, Err: = section. GetKey ("secret") if err! = Nil {panic (err)} InputnospaceS: = Strings. Replace (key. String (), "", "" ",-1) Inputnospacesupper: = Strings. ToUpper (inputnospaces) KeyS, Erro: = Base32. Stdencoding.decodestring (inputnospacesupper) if erro! = nil {fmt. Fprintln (OS. Stderr, Err. Error ()) OS. Exit (1)}//Generate a one-time password using the time at 30-second intervals epochseconds: = time. Now (). Unix () pwd: = Onetimepassword (KeyS, Tobytes (EPOCHSECONDS/30)) Secondsremaining: =-(epochseconds%) clipst r: = StrConv. The Itoa (int (pwd)) Clipboard. Writeall (CLIPSTR) fmt. Printf ("%06d (%d second (s) remaining) \ n", pwd, secondsremaining) return nil}func list () error {File,er:=os. Open ("/tmp/myga.ini") if Er!=nil && os. Isnotexist (er) {fmt. PRINTLN ("No Data!!!") Os. Exit (1)} file. Close () cfg, err: = ini. Load ("/tmp/myga.ini") if err! = Nil {panic (ERR)} sections: = cfg. Sections () for _,v: = Range Sections {fmt. Println (V.name ())} return Nil}funcDelete (item string) error {File,er:=os. Open ("/tmp/myga.ini") if Er!=nil && os. Isnotexist (er) {file, _ = os. Create ("/tmp/myga.ini")} file. Close () cfg, err: = ini. Load ("/tmp/myga.ini") if err! = Nil {panic (err)} If _, err: = cfg. GetSection (item); Err! = Nil {fmt. PRINTLN (item + "is not Exist!!!!") Os. Exit (1)} cfg. Deletesection (item) cfg. SaveTo ("/tmp/myga.ini") fmt. PRINTLN ("OK!!!") Return Nil}func Modify (item string, secret String) error {File,er:=os. Open ("/tmp/myga.ini") if Er!=nil && os. Isnotexist (er) {file, _ = os. Create ("/tmp/myga.ini")} file. Close () cfg, err: = ini. Load ("/tmp/myga.ini") if err! = Nil {panic (err)} If _, err: = cfg. GetSection (item); Err! = Nil {fmt. PRINTLN (item + "is not Exist!!!!") Os. Exit (1)} cfg. Section (item). Key ("secret"). SetValue (Secret) cfg. SaveTo ("/tmp/myga.ini") return Nil}func main () {Cli ()}
The functions implemented above have
- GA creation, UPDATE, delete, data are saved locally
- Show All Entries
- Display the verification code, copy the verification code to the sticker board
The following describes the other ways and how GA works.