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×tamp=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) }