You've already forked utilities
feat: add initial implementation of Go utilities and CI workflows
This commit is contained in:
114
Yubikey/otp.go
Executable file
114
Yubikey/otp.go
Executable file
@@ -0,0 +1,114 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user