two_factor.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. use data_encoding::BASE32;
  2. use rocket_contrib::json::Json;
  3. use serde_json;
  4. use serde_json::Value;
  5. use crate::db::{
  6. models::{TwoFactor, TwoFactorType, User},
  7. DbConn,
  8. };
  9. use crate::crypto;
  10. use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
  11. use crate::auth::Headers;
  12. use rocket::Route;
  13. pub fn routes() -> Vec<Route> {
  14. routes![
  15. get_twofactor,
  16. get_recover,
  17. recover,
  18. disable_twofactor,
  19. disable_twofactor_put,
  20. generate_authenticator,
  21. activate_authenticator,
  22. activate_authenticator_put,
  23. generate_u2f,
  24. generate_u2f_challenge,
  25. activate_u2f,
  26. activate_u2f_put,
  27. generate_yubikey,
  28. activate_yubikey,
  29. activate_yubikey_put,
  30. ]
  31. }
  32. #[get("/two-factor")]
  33. fn get_twofactor(headers: Headers, conn: DbConn) -> JsonResult {
  34. let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn);
  35. let twofactors_json: Vec<Value> = twofactors.iter().map(|c| c.to_json_list()).collect();
  36. Ok(Json(json!({
  37. "Data": twofactors_json,
  38. "Object": "list",
  39. "ContinuationToken": null,
  40. })))
  41. }
  42. #[post("/two-factor/get-recover", data = "<data>")]
  43. fn get_recover(data: JsonUpcase<PasswordData>, headers: Headers) -> JsonResult {
  44. let data: PasswordData = data.into_inner().data;
  45. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  46. err!("Invalid password");
  47. }
  48. Ok(Json(json!({
  49. "Code": headers.user.totp_recover,
  50. "Object": "twoFactorRecover"
  51. })))
  52. }
  53. #[derive(Deserialize)]
  54. #[allow(non_snake_case)]
  55. struct RecoverTwoFactor {
  56. MasterPasswordHash: String,
  57. Email: String,
  58. RecoveryCode: String,
  59. }
  60. #[post("/two-factor/recover", data = "<data>")]
  61. fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
  62. let data: RecoverTwoFactor = data.into_inner().data;
  63. use crate::db::models::User;
  64. // Get the user
  65. let mut user = match User::find_by_mail(&data.Email, &conn) {
  66. Some(user) => user,
  67. None => err!("Username or password is incorrect. Try again."),
  68. };
  69. // Check password
  70. if !user.check_valid_password(&data.MasterPasswordHash) {
  71. err!("Username or password is incorrect. Try again.")
  72. }
  73. // Check if recovery code is correct
  74. if !user.check_valid_recovery_code(&data.RecoveryCode) {
  75. err!("Recovery code is incorrect. Try again.")
  76. }
  77. // Remove all twofactors from the user
  78. for twofactor in TwoFactor::find_by_user(&user.uuid, &conn) {
  79. twofactor.delete(&conn).expect("Error deleting twofactor");
  80. }
  81. // Remove the recovery code, not needed without twofactors
  82. user.totp_recover = None;
  83. user.save(&conn)?;
  84. Ok(Json(json!({})))
  85. }
  86. #[derive(Deserialize)]
  87. #[allow(non_snake_case)]
  88. struct DisableTwoFactorData {
  89. MasterPasswordHash: String,
  90. Type: NumberOrString,
  91. }
  92. #[post("/two-factor/disable", data = "<data>")]
  93. fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
  94. let data: DisableTwoFactorData = data.into_inner().data;
  95. let password_hash = data.MasterPasswordHash;
  96. if !headers.user.check_valid_password(&password_hash) {
  97. err!("Invalid password");
  98. }
  99. let type_ = data.Type.into_i32().expect("Invalid type");
  100. if let Some(twofactor) = TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn) {
  101. twofactor.delete(&conn).expect("Error deleting twofactor");
  102. }
  103. Ok(Json(json!({
  104. "Enabled": false,
  105. "Type": type_,
  106. "Object": "twoFactorProvider"
  107. })))
  108. }
  109. #[put("/two-factor/disable", data = "<data>")]
  110. fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
  111. disable_twofactor(data, headers, conn)
  112. }
  113. #[post("/two-factor/get-authenticator", data = "<data>")]
  114. fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  115. let data: PasswordData = data.into_inner().data;
  116. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  117. err!("Invalid password");
  118. }
  119. let type_ = TwoFactorType::Authenticator as i32;
  120. let twofactor = TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn);
  121. let (enabled, key) = match twofactor {
  122. Some(tf) => (true, tf.data),
  123. _ => (false, BASE32.encode(&crypto::get_random(vec![0u8; 20]))),
  124. };
  125. Ok(Json(json!({
  126. "Enabled": enabled,
  127. "Key": key,
  128. "Object": "twoFactorAuthenticator"
  129. })))
  130. }
  131. #[derive(Deserialize, Debug)]
  132. #[allow(non_snake_case)]
  133. struct EnableAuthenticatorData {
  134. MasterPasswordHash: String,
  135. Key: String,
  136. Token: NumberOrString,
  137. }
  138. #[post("/two-factor/authenticator", data = "<data>")]
  139. fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
  140. let data: EnableAuthenticatorData = data.into_inner().data;
  141. let password_hash = data.MasterPasswordHash;
  142. let key = data.Key;
  143. let token = match data.Token.into_i32() {
  144. Some(n) => n as u64,
  145. None => err!("Malformed token"),
  146. };
  147. if !headers.user.check_valid_password(&password_hash) {
  148. err!("Invalid password");
  149. }
  150. // Validate key as base32 and 20 bytes length
  151. let decoded_key: Vec<u8> = match BASE32.decode(key.as_bytes()) {
  152. Ok(decoded) => decoded,
  153. _ => err!("Invalid totp secret"),
  154. };
  155. if decoded_key.len() != 20 {
  156. err!("Invalid key length")
  157. }
  158. let type_ = TwoFactorType::Authenticator;
  159. let twofactor = TwoFactor::new(headers.user.uuid.clone(), type_, key.to_uppercase());
  160. // Validate the token provided with the key
  161. if !twofactor.check_totp_code(token) {
  162. err!("Invalid totp code")
  163. }
  164. let mut user = headers.user;
  165. _generate_recover_code(&mut user, &conn);
  166. twofactor.save(&conn).expect("Error saving twofactor");
  167. Ok(Json(json!({
  168. "Enabled": true,
  169. "Key": key,
  170. "Object": "twoFactorAuthenticator"
  171. })))
  172. }
  173. #[put("/two-factor/authenticator", data = "<data>")]
  174. fn activate_authenticator_put(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
  175. activate_authenticator(data, headers, conn)
  176. }
  177. fn _generate_recover_code(user: &mut User, conn: &DbConn) {
  178. if user.totp_recover.is_none() {
  179. let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
  180. user.totp_recover = Some(totp_recover);
  181. user.save(conn).ok();
  182. }
  183. }
  184. use u2f::messages::{RegisterResponse, SignResponse, U2fSignRequest};
  185. use u2f::protocol::{Challenge, U2f};
  186. use u2f::register::Registration;
  187. use crate::CONFIG;
  188. const U2F_VERSION: &str = "U2F_V2";
  189. lazy_static! {
  190. static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain);
  191. static ref U2F: U2f = U2f::new(APP_ID.clone());
  192. }
  193. #[post("/two-factor/get-u2f", data = "<data>")]
  194. fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  195. if !CONFIG.domain_set {
  196. err!("`DOMAIN` environment variable is not set. U2F disabled")
  197. }
  198. let data: PasswordData = data.into_inner().data;
  199. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  200. err!("Invalid password");
  201. }
  202. let user_uuid = &headers.user.uuid;
  203. let u2f_type = TwoFactorType::U2f as i32;
  204. let enabled = TwoFactor::find_by_user_and_type(user_uuid, u2f_type, &conn).is_some();
  205. Ok(Json(json!({
  206. "Enabled": enabled,
  207. "Object": "twoFactorU2f"
  208. })))
  209. }
  210. #[post("/two-factor/get-u2f-challenge", data = "<data>")]
  211. fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  212. let data: PasswordData = data.into_inner().data;
  213. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  214. err!("Invalid password");
  215. }
  216. let user_uuid = &headers.user.uuid;
  217. let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fRegisterChallenge, &conn).challenge;
  218. Ok(Json(json!({
  219. "UserId": headers.user.uuid,
  220. "AppId": APP_ID.to_string(),
  221. "Challenge": challenge,
  222. "Version": U2F_VERSION,
  223. })))
  224. }
  225. #[derive(Deserialize, Debug)]
  226. #[allow(non_snake_case)]
  227. struct EnableU2FData {
  228. MasterPasswordHash: String,
  229. DeviceResponse: String,
  230. }
  231. // This struct is copied from the U2F lib
  232. // because challenge is not always sent
  233. #[derive(Deserialize)]
  234. #[serde(rename_all = "camelCase")]
  235. struct RegisterResponseCopy {
  236. pub registration_data: String,
  237. pub version: String,
  238. pub challenge: Option<String>,
  239. pub error_code: Option<NumberOrString>,
  240. pub client_data: String,
  241. }
  242. impl RegisterResponseCopy {
  243. fn into_response(self, challenge: String) -> RegisterResponse {
  244. RegisterResponse {
  245. registration_data: self.registration_data,
  246. version: self.version,
  247. challenge,
  248. client_data: self.client_data,
  249. }
  250. }
  251. }
  252. #[post("/two-factor/u2f", data = "<data>")]
  253. fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
  254. let data: EnableU2FData = data.into_inner().data;
  255. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  256. err!("Invalid password");
  257. }
  258. let tf_challenge =
  259. TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::U2fRegisterChallenge as i32, &conn);
  260. if let Some(tf_challenge) = tf_challenge {
  261. let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
  262. tf_challenge.delete(&conn)?;
  263. let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
  264. let error_code = response_copy
  265. .error_code
  266. .clone()
  267. .map_or("0".into(), NumberOrString::into_string);
  268. if error_code != "0" {
  269. err!("Error registering U2F token")
  270. }
  271. let response = response_copy.into_response(challenge.challenge.clone());
  272. let registration = U2F.register_response(challenge.clone(), response)?;
  273. // TODO: Allow more than one U2F device
  274. let mut registrations = Vec::new();
  275. registrations.push(registration);
  276. let tf_registration = TwoFactor::new(
  277. headers.user.uuid.clone(),
  278. TwoFactorType::U2f,
  279. serde_json::to_string(&registrations).unwrap(),
  280. );
  281. tf_registration.save(&conn)?;
  282. let mut user = headers.user;
  283. _generate_recover_code(&mut user, &conn);
  284. Ok(Json(json!({
  285. "Enabled": true,
  286. "Challenge": {
  287. "UserId": user.uuid,
  288. "AppId": APP_ID.to_string(),
  289. "Challenge": challenge,
  290. "Version": U2F_VERSION,
  291. },
  292. "Object": "twoFactorU2f"
  293. })))
  294. } else {
  295. err!("Can't recover challenge")
  296. }
  297. }
  298. #[put("/two-factor/u2f", data = "<data>")]
  299. fn activate_u2f_put(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn) -> JsonResult {
  300. activate_u2f(data, headers, conn)
  301. }
  302. fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge {
  303. let challenge = U2F.generate_challenge().unwrap();
  304. TwoFactor::new(user_uuid.into(), type_, serde_json::to_string(&challenge).unwrap())
  305. .save(conn)
  306. .expect("Error saving challenge");
  307. challenge
  308. }
  309. // This struct is copied from the U2F lib
  310. // because it doesn't implement Deserialize
  311. #[derive(Serialize, Deserialize, Clone)]
  312. #[serde(rename_all = "camelCase")]
  313. struct RegistrationCopy {
  314. pub key_handle: Vec<u8>,
  315. pub pub_key: Vec<u8>,
  316. pub attestation_cert: Option<Vec<u8>>,
  317. }
  318. impl Into<Registration> for RegistrationCopy {
  319. fn into(self) -> Registration {
  320. Registration {
  321. key_handle: self.key_handle,
  322. pub_key: self.pub_key,
  323. attestation_cert: self.attestation_cert,
  324. }
  325. }
  326. }
  327. fn _parse_registrations(registations: &str) -> Vec<Registration> {
  328. let registrations_copy: Vec<RegistrationCopy> =
  329. serde_json::from_str(registations).expect("Can't parse RegistrationCopy data");
  330. registrations_copy.into_iter().map(Into::into).collect()
  331. }
  332. pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRequest> {
  333. let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn);
  334. let type_ = TwoFactorType::U2f as i32;
  335. let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn) {
  336. Some(tf) => tf,
  337. None => err!("No U2F devices registered"),
  338. };
  339. let registrations = _parse_registrations(&twofactor.data);
  340. let signed_request: U2fSignRequest = U2F.sign_request(challenge, registrations);
  341. Ok(signed_request)
  342. }
  343. pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
  344. let challenge_type = TwoFactorType::U2fLoginChallenge as i32;
  345. let u2f_type = TwoFactorType::U2f as i32;
  346. let tf_challenge = TwoFactor::find_by_user_and_type(user_uuid, challenge_type, &conn);
  347. let challenge = match tf_challenge {
  348. Some(tf_challenge) => {
  349. let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
  350. tf_challenge.delete(&conn)?;
  351. challenge
  352. }
  353. None => err!("Can't recover login challenge"),
  354. };
  355. let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, u2f_type, conn) {
  356. Some(tf) => tf,
  357. None => err!("No U2F devices registered"),
  358. };
  359. let registrations = _parse_registrations(&twofactor.data);
  360. let response: SignResponse = serde_json::from_str(response)?;
  361. let mut _counter: u32 = 0;
  362. for registration in registrations {
  363. let response = U2F.sign_response(challenge.clone(), registration, response.clone(), _counter);
  364. match response {
  365. Ok(new_counter) => {
  366. _counter = new_counter;
  367. info!("O {:#}", new_counter);
  368. return Ok(());
  369. }
  370. Err(e) => {
  371. info!("E {:#}", e);
  372. break;
  373. }
  374. }
  375. }
  376. err!("error verifying response")
  377. }
  378. #[derive(Deserialize, Debug)]
  379. #[allow(non_snake_case)]
  380. struct EnableYubikeyData {
  381. MasterPasswordHash: String,
  382. Key1: Option<String>,
  383. Key2: Option<String>,
  384. Key3: Option<String>,
  385. Key4: Option<String>,
  386. Key5: Option<String>,
  387. Nfc: bool,
  388. }
  389. #[derive(Deserialize, Serialize, Debug)]
  390. #[allow(non_snake_case)]
  391. pub struct YubikeyMetadata {
  392. Keys: Vec<String>,
  393. pub Nfc: bool,
  394. }
  395. use yubico::config::Config;
  396. use yubico::Yubico;
  397. fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
  398. let mut yubikeys: Vec<String> = Vec::new();
  399. if data.Key1.is_some() {
  400. yubikeys.push(data.Key1.as_ref().unwrap().to_owned());
  401. }
  402. if data.Key2.is_some() {
  403. yubikeys.push(data.Key2.as_ref().unwrap().to_owned());
  404. }
  405. if data.Key3.is_some() {
  406. yubikeys.push(data.Key3.as_ref().unwrap().to_owned());
  407. }
  408. if data.Key4.is_some() {
  409. yubikeys.push(data.Key4.as_ref().unwrap().to_owned());
  410. }
  411. if data.Key5.is_some() {
  412. yubikeys.push(data.Key5.as_ref().unwrap().to_owned());
  413. }
  414. yubikeys
  415. }
  416. fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
  417. let mut result = json!({});
  418. for (i, key) in yubikeys.into_iter().enumerate() {
  419. result[format!("Key{}", i + 1)] = Value::String(key);
  420. }
  421. result
  422. }
  423. fn verify_yubikey_otp(otp: String) -> JsonResult {
  424. if !CONFIG.yubico_cred_set {
  425. err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
  426. }
  427. let yubico = Yubico::new();
  428. let config = Config::default()
  429. .set_client_id(CONFIG.yubico_client_id.to_owned())
  430. .set_key(CONFIG.yubico_secret_key.to_owned());
  431. let result = match CONFIG.yubico_server {
  432. Some(ref server) => yubico.verify(otp, config.set_api_hosts(vec![server.to_owned()])),
  433. None => yubico.verify(otp, config),
  434. };
  435. match result {
  436. Ok(_answer) => Ok(Json(json!({}))),
  437. Err(_e) => err!("Failed to verify OTP"),
  438. }
  439. }
  440. #[post("/two-factor/get-yubikey", data = "<data>")]
  441. fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
  442. if !CONFIG.yubico_cred_set {
  443. err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
  444. }
  445. let data: PasswordData = data.into_inner().data;
  446. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  447. err!("Invalid password");
  448. }
  449. let user_uuid = &headers.user.uuid;
  450. let yubikey_type = TwoFactorType::YubiKey as i32;
  451. let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn);
  452. if let Some(r) = r {
  453. let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
  454. let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
  455. result["Enabled"] = Value::Bool(true);
  456. result["Nfc"] = Value::Bool(yubikey_metadata.Nfc);
  457. result["Object"] = Value::String("twoFactorU2f".to_owned());
  458. Ok(Json(result))
  459. } else {
  460. Ok(Json(json!({
  461. "Enabled": false,
  462. "Object": "twoFactorU2f",
  463. })))
  464. }
  465. }
  466. #[post("/two-factor/yubikey", data = "<data>")]
  467. fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
  468. let data: EnableYubikeyData = data.into_inner().data;
  469. if !headers.user.check_valid_password(&data.MasterPasswordHash) {
  470. err!("Invalid password");
  471. }
  472. // Check if we already have some data
  473. let yubikey_data = TwoFactor::find_by_user_and_type(&headers.user.uuid, TwoFactorType::YubiKey as i32, &conn);
  474. if let Some(yubikey_data) = yubikey_data {
  475. yubikey_data.delete(&conn)?;
  476. }
  477. let yubikeys = parse_yubikeys(&data);
  478. if yubikeys.is_empty() {
  479. return Ok(Json(json!({
  480. "Enabled": false,
  481. "Object": "twoFactorU2f",
  482. })));
  483. }
  484. // Ensure they are valid OTPs
  485. for yubikey in &yubikeys {
  486. if yubikey.len() == 12 {
  487. // YubiKey ID
  488. continue;
  489. }
  490. let result = verify_yubikey_otp(yubikey.to_owned());
  491. if let Err(_e) = result {
  492. err!("Invalid Yubikey OTP provided");
  493. }
  494. }
  495. let yubikey_ids: Vec<String> = yubikeys.into_iter().map(|x| (&x[..12]).to_owned()).collect();
  496. let yubikey_metadata = YubikeyMetadata {
  497. Keys: yubikey_ids,
  498. Nfc: data.Nfc,
  499. };
  500. let yubikey_registration = TwoFactor::new(
  501. headers.user.uuid.clone(),
  502. TwoFactorType::YubiKey,
  503. serde_json::to_string(&yubikey_metadata).unwrap(),
  504. );
  505. yubikey_registration.save(&conn)?;
  506. let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
  507. result["Enabled"] = Value::Bool(true);
  508. result["Nfc"] = Value::Bool(yubikey_metadata.Nfc);
  509. result["Object"] = Value::String("twoFactorU2f".to_owned());
  510. Ok(Json(result))
  511. }
  512. #[put("/two-factor/yubikey", data = "<data>")]
  513. fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
  514. activate_yubikey(data, headers, conn)
  515. }
  516. pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
  517. if response.len() != 44 {
  518. err!("Invalid Yubikey OTP length");
  519. }
  520. let yubikey_type = TwoFactorType::YubiKey as i32;
  521. let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn) {
  522. Some(tf) => tf,
  523. None => err!("No YubiKey devices registered"),
  524. };
  525. let yubikey_metadata: YubikeyMetadata =
  526. serde_json::from_str(&twofactor.data).expect("Can't parse Yubikey Metadata");
  527. let response_id = &response[..12];
  528. if !yubikey_metadata.Keys.contains(&response_id.to_owned()) {
  529. err!("Given Yubikey is not registered");
  530. }
  531. let result = verify_yubikey_otp(response.to_owned());
  532. match result {
  533. Ok(_answer) => Ok(()),
  534. Err(_e) => err!("Failed to verify Yubikey against OTP server"),
  535. }
  536. }