gauth/gauth.go
M. J. Fromberger 3e14d15f54 Clean up output formatting code.
Instead of manually computing column sizes, use the tabwriter library package.
The output is the same, but requires less fiddly space manipulation.
2019-06-02 08:23:28 -07:00

113 lines
2.5 KiB
Go

package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/csv"
"fmt"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"strings"
"syscall"
"text/tabwriter"
"github.com/pcarrier/gauth/gauth"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
user, e := user.Current()
if e != nil {
log.Fatal(e)
}
cfgPath := path.Join(user.HomeDir, ".config/gauth.csv")
cfgContent, e := ioutil.ReadFile(cfgPath)
if e != nil {
log.Fatal(e)
}
// Support for 'openssl enc -aes-128-cbc -md sha256 -pass pass:'
if bytes.HasPrefix(cfgContent, []byte("Salted__")) {
fmt.Printf("Encryption password: ")
passwd, e := terminal.ReadPassword(int(syscall.Stdin))
fmt.Printf("\n")
if e != nil {
log.Fatal(e)
}
salt := cfgContent[8:16]
rest := cfgContent[16:]
salting := sha256.New()
salting.Write([]byte(passwd))
salting.Write(salt)
sum := salting.Sum(nil)
key := sum[:16]
iv := sum[16:]
block, e := aes.NewCipher(key)
if e != nil {
log.Fatal(e)
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(rest, rest)
// Remove padding
i := len(rest) - 1
for rest[i] < 16 {
i--
}
cfgContent = rest[:i]
}
cfgReader := csv.NewReader(bytes.NewReader(cfgContent))
// Unix-style tabular
cfgReader.Comma = ':'
cfg, e := cfgReader.ReadAll()
if e != nil {
log.Fatal(e)
}
currentTS, progress := gauth.IndexNow()
prevTS := currentTS - 1
nextTS := currentTS + 1
tw := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0)
fmt.Fprintln(tw, "\tprev\tcurr\tnext")
for _, record := range cfg {
name := record[0]
secret := normalizeSecret(record[1])
prevToken := authCodeOrDie(secret, prevTS)
currentToken := authCodeOrDie(secret, currentTS)
nextToken := authCodeOrDie(secret, nextTS)
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", name, prevToken, currentToken, nextToken)
}
tw.Flush()
fmt.Printf("[%-29s]\n", strings.Repeat("=", progress))
}
// normalizeSecret cleans up whitespace and adds any missing padding to sec to
// use it as an OTP seed.
func normalizeSecret(sec string) string {
noPadding := strings.ToUpper(strings.Replace(sec, " ", "", -1))
padLength := 8 - (len(noPadding) % 8)
if padLength < 8 {
return noPadding + strings.Repeat("=", padLength)
}
return noPadding
}
// authCodeOrDie returns a code for the specified parameters, or aborts if an
// error occurred while generating the code.
func authCodeOrDie(sec string, ts int64) string {
str, e := gauth.Code(sec, ts)
if e != nil {
log.Fatal(e)
}
return str
}