Files
utilities/Yubikey/otp.go

115 lines
2.6 KiB
Go
Executable File

package Yubikey
import (
"errors"
"fmt"
"log"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
)
type Response struct {
h string
t time.Time
otp string
presentedNonce string
nonce string
sl uint8
timestamp uint32
sessionCounter uint32
sessionUse uint32
status string
}
const keyLen = 40
func (response *Response) Time() time.Time {
return response.t
}
func (response *Response) TimeStamp() uint32 {
return response.timestamp
}
func (response *Response) Valid() bool {
return response.status == "OK" && response.nonce == response.presentedNonce
}
//goland:noinspection GoUnusedExportedFunction // library function
func GetVerification(otp string, clientId string) (*Response, error) {
nonce := randSeq(keyLen)
url := fmt.Sprintf(
"https://api.yubico.com/wsapi/2.0/verify?id=%s&otp=%s&timestamp=1&nonce=%s",
clientId,
otp,
nonce,
)
response, _ := http.Get(url)
defer func() {
_ = response.Body.Close()
}()
body := make([]byte, response.ContentLength)
_, err := response.Body.Read(body)
if err != nil {
log.Fatal(err)
}
parts := strings.Split(string(body), "\r\n")
var status string
for k, part := range parts {
if strings.Contains(part, "status=") {
status = strings.Replace(parts[k], "status=", "", 1)
break
}
}
if status != "OK" {
return nil, errors.New("invalid response status: " + status)
}
timeString := strings.Replace(parts[1], "t=", "", 1)[0:20]
signedTime, err := time.Parse(time.RFC3339, timeString)
if err != nil {
println(err)
}
sl, _ := strconv.ParseInt(strings.Replace(parts[4], "sl=", "", 1), 10, 8)
timestamp, _ := strconv.ParseInt(strings.Replace(parts[5], "timestamp=", "", 1), 10, 32)
sessionCounter, _ := strconv.ParseInt(strings.Replace(parts[6], "sessioncounter=", "", 1), 10, 32)
sessionUse, _ := strconv.ParseInt(strings.Replace(parts[7], "sessionuse=", "", 1), 10, 32)
keyResponse := Response{
h: strings.Replace(parts[0], "h=", "", 1),
t: signedTime,
otp: strings.Replace(parts[2], "otp=", "", 1),
presentedNonce: nonce,
nonce: strings.Replace(parts[3], "nonce=", "", 1),
sl: uint8(sl),
timestamp: uint32(timestamp),
sessionCounter: uint32(sessionCounter),
sessionUse: uint32(sessionUse),
status: status,
}
return &keyResponse, nil
}
func randSeq(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}