package main import ( "C" "crypto/hmac" "crypto/sha1" "crypto/sha256" "crypto/sha512" "github.com/creachadair/otp" "hash" "log" "os" "runtime" "strconv" "strings" "time" ) var token string var configs map[string]otp.Config func getSha1() func() hash.Hash { return sha1.New } //export loadConfigs func loadConfigs() { var homeDir string osVar := runtime.GOOS if osVar == "windows" { homeDir = os.Getenv("USERPROFILE") } else { homeDir = os.Getenv("HOME") } configs = make(map[string]otp.Config) files, err := os.ReadDir(homeDir + "/.totp") if err != nil { log.Fatal(err) } for _, fileEntry := range files { fileBytes, err := os.ReadFile(homeDir + "/.totp/" + fileEntry.Name()) if err != nil { log.Fatal(err) } fileStr := string(fileBytes) lineSplt := strings.Split(fileStr, "\n") var key string var hashVar func() hash.Hash var timeStep func() uint64 var counter uint64 var digits int var format func(hash []byte, length int) string for _, line := range lineSplt { kv := strings.Split(line, ":") if len(kv) < 2 { continue } kv[1] = strings.TrimSpace(kv[1]) switch strings.ToLower(kv[0]) { case "key": key = kv[1] case "hash": { switch kv[1] { case "sha1": hashVar = getSha1() case "sha256": hashVar = func() hash.Hash { return sha256.New() } case "sha512": hashVar = func() hash.Hash { return sha512.New() } case "hmac": { keyBytes, err := otp.ParseKey(key) if err != nil { log.Fatal(err) } hashVar = func() hash.Hash { return hmac.New(getSha1(), keyBytes) } // Probably not proper implementation but who uses HMAC anyway } } } case "timestep": { ts, err := strconv.Atoi(kv[1]) if err != nil { log.Fatal(err) } timeStep = otp.TimeWindow(ts) } case "counter": { cNum, err := strconv.ParseUint(kv[1], 10, 0) if err != nil { log.Fatal(err) } if cNum < 6 || cNum > 10 { log.Fatal("Counter not in range 6-10") } counter = cNum } case "format": { } //TODO implement } } config := otp.Config{ Key: key, Hash: hashVar, TimeStep: timeStep, Counter: counter, Digits: digits, Format: format, } configs[fileEntry.Name()] = config } } //export getConfigNames func getConfigNames() []string { names := make([]string, 0, len(configs)) for name := range configs { names = append(names, name) } return names } //export getCode func getCode(configName string) *C.char { code := configs[configName].TOTP() cCode := C.CString(code) runtime.KeepAlive(code) return cCode } //export getTimeRemaining func getTimeRemaining(n int) uint64 { return uint64(n) - (uint64(time.Now().Unix()) % uint64(n)) } //export getTimeRemainingMS func getTimeRemainingMS(n int) uint64 { return (uint64(n) * 1000) - (uint64(time.Now().UnixNano()/1e6) % (uint64(n) * 1000)) } func main() {}