// NewYubiKey takes the key and initial OTP and returns an // authenticator. func NewYubiKey(key []byte, initialOTP string) (*Authenticator, error) { pub, otp, err := yubikey.ParseOTPString(initialOTP) if err != nil { return nil, err } tmpKey := yubikey.NewKey(key) token, err := otp.Parse(tmpKey) if err != nil { return nil, err } util.Zero(tmpKey[:]) config := &YubiKeyConfig{ Counter: getTokenCounter(token), Key: key, Public: pub, } defer util.Zero(config.Key[:]) auth := &Authenticator{ Type: TypeYubiKey, Last: initialOTP, Secret: config.Bytes(), } return auth, nil }
// TestBuildUserToken builds the test user token from an OTP. func TestBuildUserToken(t *testing.T) { key, err := hex.DecodeString(testYubiKey) if err != nil { t.Fatalf("%v", err) } tmpKey := yubikey.NewKey(key) _, otp, err := yubikey.ParseOTPString(testInitialYKOTP) if err != nil { t.Fatalf("%v", err) } yubiToken, err = otp.Parse(tmpKey) if err != nil { t.Fatalf("%v", err) } }
// ValidateYubiKey takes an Authenticator that is presumed to be a // YubiKey authenticator and attempts to validate the given OTP // using it. The YubiKey authenticator will always need to be updated // when successful to account for changes in the counter, and to // update the last OTP. func ValidateYubiKey(auth *Authenticator, otp string) (bool, error) { if (auth == nil) || (auth.Type != TypeYubiKey) { return false, ErrInvalidAuthenticator } if auth.Last == otp { return false, ErrValidationFail } config, err := ParseYubiKeyConfig(auth.Secret) if err != nil { return false, ErrInvalidAuthenticator } tmpKey := yubikey.NewKey(config.Key) defer util.Zero(tmpKey[:]) pub, ykOTP, err := yubikey.ParseOTPString(otp) if err != nil { return false, ErrValidationFail } if !bytes.Equal(pub, config.Public) { return false, ErrValidationFail } userToken, err := ykOTP.Parse(tmpKey) if err != nil { return false, ErrValidationFail } if getTokenCounter(userToken) < config.Counter { return false, ErrValidationFail } config.Counter = getTokenCounter(userToken) auth.Last = otp auth.Secret = config.Bytes() return true, nil }
func decryptHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() Metrics.Requests.Add(1) otp := r.FormValue("otp") if otp == "" { Metrics.Errors.Add(1) glog.Info("ERR No OTP provided") http.Error(w, "ERR No OTP provided", http.StatusOK) return } if len(otp) < 32 || len(otp) > 48 || !yubikey.ModHexP([]byte(otp)) { Metrics.Errors.Add(1) glog.Info("ERR Invalid OTP format: ", otp) http.Error(w, "ERR Invalid OTP format", http.StatusOK) return } pubid, yotp, err := yubikey.ParseOTPString(otp) stmt, err := KeysDB.Prepare("SELECT aeskey, internalname FROM yubikeys WHERE publicname = ? AND active = 1") if err != nil { Metrics.Errors.Add(1) glog.Error("ERR DB error during Prepare(): ", err) http.Error(w, "ERR Database error", http.StatusOK) return } defer stmt.Close() var aeskeyHex string var name string err = stmt.QueryRow(string(pubid)).Scan(&aeskeyHex, &name) if err != nil { Metrics.Errors.Add(1) if err == sql.ErrNoRows { glog.Info("ERR Unknown yubikey: ", string(pubid)) http.Error(w, "ERR Unknown yubikey", http.StatusOK) } else { glog.Error("ERR DB error during SELECT: ", err) http.Error(w, "ERR Database error", http.StatusOK) } return } var aesKey yubikey.Key hex.Decode(aesKey[:], []byte(aeskeyHex)) // error ignored, we trust the database token, err := yotp.Parse(aesKey) if err != nil { Metrics.Errors.Add(1) glog.Info("ERR Corrupt OTP (Parse failed): ", otp) http.Error(w, "ERR Corrupt OTP", http.StatusOK) return } nameBytes, _ /* err */ := hex.DecodeString(name) // error ignored, we trust the database if !bytes.Equal(nameBytes, token.Uid[:]) { Metrics.Errors.Add(1) glog.Warning("ERR Corrupt OTP (UID mismatch): ", otp) http.Error(w, "ERR Corrupt OTP", http.StatusOK) return } response := fmt.Sprintf("OK counter=%04x low=%04x high=%02x use=%02x", token.Ctr, token.Tstpl, token.Tstph, token.Use) glog.Info(response) fmt.Fprintf(w, response) }