Skip to content

Instantly share code, notes, and snippets.

@tilaklodha
Created April 14, 2018 09:29
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save tilaklodha/248eb5618ae4f66e96e1ced89fcf4922 to your computer and use it in GitHub Desktop.
Save tilaklodha/248eb5618ae4f66e96e1ced89fcf4922 to your computer and use it in GitHub Desktop.
Simple Google Authenticator script in go
// Checkout the readme for this here: https://github.com/tilaklodha/google-authenticator
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/base32"
"encoding/binary"
"fmt"
"io/ioutil"
"os/exec"
"strconv"
"strings"
"time"
)
//Panic if error is not nil
func check(e error) {
if e != nil {
panic(e)
}
}
//Append extra 0s if the length of otp is less than 6
//If otp is "1234", it will return it as "001234"
func prefix0(otp string) string {
if len(otp) == 6 {
return otp
}
for i := (6 - len(otp)); i > 0; i-- {
otp = "0" + otp
}
return otp
}
func getHOTPToken(secret string, interval int64) string {
//Converts secret to base32 Encoding. Base32 encoding desires a 32-character
//subset of the twenty-six letters A–Z and ten digits 0–9
key, err := base32.StdEncoding.DecodeString(strings.ToUpper(secret))
check(err)
bs := make([]byte, 8)
binary.BigEndian.PutUint64(bs, uint64(interval))
//Signing the value using HMAC-SHA1 Algorithm
hash := hmac.New(sha1.New, key)
hash.Write(bs)
h := hash.Sum(nil)
// We're going to use a subset of the generated hash.
// Using the last nibble (half-byte) to choose the index to start from.
// This number is always appropriate as it's maximum decimal 15, the hash will
// have the maximum index 19 (20 bytes of SHA1) and we need 4 bytes.
o := (h[19] & 15)
var header uint32
//Get 32 bit chunk from hash starting at the o
r := bytes.NewReader(h[o : o+4])
err = binary.Read(r, binary.BigEndian, &header)
check(err)
//Ignore most significant bits as per RFC 4226.
//Takes division from one million to generate a remainder less than < 7 digits
h12 := (int(header) & 0x7fffffff) % 1000000
//Converts number as a string
otp := strconv.Itoa(int(h12))
return prefix0(otp)
}
func getTOTPToken(secret string) string {
//The TOTP token is just a HOTP token seeded with every 30 seconds.
interval := time.Now().Unix() / 30
return getHOTPToken(secret, interval)
}
func main() {
//Read the secret token from file system
data, err := ioutil.ReadFile("secret.pem")
check(err)
secret := string(data)
otp := getTOTPToken(secret)
//Copies the otp generated to your clipboard
err = exec.Command("bash", "-c", fmt.Sprintf("echo %s | tr -d \"\n, \" | pbcopy", otp)).Run()
check(err)
}
dummysecretdummy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment