200 lines
4.0 KiB
Go
200 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"C"
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"github.com/creachadair/otp"
|
|
"hash"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
var token string
|
|
var homeDir string
|
|
|
|
var configs map[string]otp.Config
|
|
|
|
func getSha1() func() hash.Hash { return sha1.New }
|
|
|
|
//export loadConfigs
|
|
func loadConfigs() {
|
|
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 "digits":
|
|
{
|
|
dNum, err := strconv.Atoi(kv[1])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if dNum < 1 || dNum > 10 {
|
|
log.Fatal("Digits is not in range 1-10")
|
|
}
|
|
digits = dNum
|
|
}
|
|
case "format":
|
|
{
|
|
} //TODO implement
|
|
}
|
|
}
|
|
|
|
config := otp.Config{
|
|
Hash: hashVar,
|
|
TimeStep: timeStep,
|
|
Counter: counter,
|
|
Digits: digits,
|
|
Format: format,
|
|
}
|
|
err = config.ParseKey(key)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fileName := fileEntry.Name()
|
|
ext := filepath.Ext(fileName)
|
|
|
|
configs[strings.TrimSuffix(fileName, ext)] = config
|
|
}
|
|
}
|
|
|
|
//export getConfigNames
|
|
func getConfigNames() (**C.char, C.int) {
|
|
configsLen := len(configs)
|
|
cArray := C.malloc(C.size_t(configsLen) * C.size_t(unsafe.Sizeof(uintptr(0))))
|
|
|
|
ar := unsafe.Slice((**C.char)(cArray), configsLen)
|
|
i := 0
|
|
for name := range configs {
|
|
ar[i] = C.CString(name)
|
|
i++
|
|
}
|
|
|
|
return (**C.char)(cArray), C.int(configsLen)
|
|
}
|
|
|
|
//export getConfig
|
|
func getConfig(configName string) (*C.char, C.int) {
|
|
files, err := os.ReadDir(homeDir + "/.totp")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
for _, fileEntry := range files {
|
|
if strings.HasPrefix(fileEntry.Name(), configName) {
|
|
fileBytes, err := os.ReadFile(homeDir + "/.totp/" + fileEntry.Name())
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fileStr := string(fileBytes)
|
|
cStr := C.CString(fileStr)
|
|
runtime.KeepAlive(fileStr)
|
|
return (*C.char)(cStr), (C.int)(len(fileStr))
|
|
}
|
|
}
|
|
return nil, -1
|
|
}
|
|
|
|
//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() {}
|