115 lines
2.6 KiB
Go
Executable File
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×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)
|
|
}
|