1use crate::credential::totp::{Totp, TotpAlgo, TotpDigits};
2use crate::idm::server::IdmServerProxyReadTransaction;
3use crate::prelude::*;
4use crate::server::identity::Identity;
5use crate::server::QueryServerTransaction;
6use kanidm_proto::internal::IdentifyUserResponse;
7use openssl::ec::EcKey;
8use openssl::pkey::{PKey, Private, Public};
9use openssl::pkey_ctx::PkeyCtx;
10use std::sync::Arc;
11use uuid::Uuid;
12
13static TOTP_STEP: u64 = 300;
16
17#[derive(Debug)]
18pub struct IdentifyUserStartEvent {
19 pub target: Uuid,
20 pub ident: Identity,
21}
22
23impl IdentifyUserStartEvent {
24 pub fn new(target: Uuid, ident: Identity) -> Self {
25 IdentifyUserStartEvent { target, ident }
26 }
27}
28pub struct IdentifyUserDisplayCodeEvent {
29 pub target: Uuid,
30 pub ident: Identity,
31}
32
33impl IdentifyUserDisplayCodeEvent {
34 pub fn new(target: Uuid, ident: Identity) -> Self {
35 IdentifyUserDisplayCodeEvent { target, ident }
36 }
37}
38
39pub struct IdentifyUserSubmitCodeEvent {
40 pub code: u32,
41 pub target: Uuid,
42 pub ident: Identity,
43}
44
45impl IdentifyUserSubmitCodeEvent {
46 pub fn new(target: Uuid, ident: Identity, code: u32) -> Self {
47 IdentifyUserSubmitCodeEvent {
48 target,
49 ident,
50 code,
51 }
52 }
53}
54
55impl IdmServerProxyReadTransaction<'_> {
56 pub fn handle_identify_user_start(
57 &mut self,
58 IdentifyUserStartEvent { target, ident }: &IdentifyUserStartEvent,
59 current_time: Duration,
60 ) -> Result<IdentifyUserResponse, OperationError> {
61 let (ident_entry, target_entry) = match self.get_involved_entries(ident, *target) {
62 Ok(tuple) => tuple,
63 Err(early_response) => return Ok(early_response),
64 };
65
66 let response = if ident_entry.get_uuid() < target_entry.get_uuid() {
67 IdentifyUserResponse::WaitForCode
68 } else {
69 let totp_secret = self.get_self_totp(&ident_entry, &target_entry)?;
70
71 let totp_value = totp_secret
72 .do_totp_duration_from_epoch(¤t_time)
73 .map_err(|_| OperationError::CryptographyError)?;
74
75 IdentifyUserResponse::ProvideCode {
76 step: TOTP_STEP as u32,
77 totp: totp_value,
78 }
79 };
80 Ok(response)
81 }
82
83 pub fn handle_identify_user_display_code(
84 &mut self,
85 IdentifyUserDisplayCodeEvent { target, ident }: &IdentifyUserDisplayCodeEvent,
86 current_time: Duration,
87 ) -> Result<IdentifyUserResponse, OperationError> {
88 let (ident_entry, target_entry) = match self.get_involved_entries(ident, *target) {
89 Ok(tuple) => tuple,
90 Err(early_response) => return Ok(early_response),
91 };
92
93 let totp_secret = self.get_self_totp(&ident_entry, &target_entry)?;
94
95 let totp_value = totp_secret
96 .do_totp_duration_from_epoch(¤t_time)
97 .map_err(|_| OperationError::CryptographyError)?;
98
99 Ok(IdentifyUserResponse::ProvideCode {
100 step: TOTP_STEP as u32,
101 totp: totp_value,
102 })
103 }
104
105 pub fn handle_identify_user_submit_code(
106 &mut self,
107 IdentifyUserSubmitCodeEvent {
108 target,
109 ident,
110 code,
111 }: &IdentifyUserSubmitCodeEvent,
112 current_time: Duration,
113 ) -> Result<IdentifyUserResponse, OperationError> {
114 let (ident_entry, target_entry) = match self.get_involved_entries(ident, *target) {
115 Ok(tuple) => tuple,
116 Err(early_response) => return Ok(early_response),
117 };
118
119 let totp_secret = self.get_user_totp(&ident_entry, &target_entry)?;
120
121 if !totp_secret.verify(*code, current_time) {
122 return Ok(IdentifyUserResponse::CodeFailure);
123 }
124
125 let response = if ident_entry.get_uuid() < target_entry.get_uuid() {
128 let totp_secret = self.get_self_totp(&ident_entry, &target_entry)?;
129 let totp_value = totp_secret
130 .do_totp_duration_from_epoch(¤t_time)
131 .map_err(|_| OperationError::CryptographyError)?;
132 IdentifyUserResponse::ProvideCode {
133 step: TOTP_STEP as u32,
134 totp: totp_value,
135 }
136 } else {
137 IdentifyUserResponse::Success
138 };
139 Ok(response)
140 }
141
142 fn get_involved_entries(
145 &mut self,
146 ident: &Identity,
147 target: Uuid,
148 ) -> Result<(Arc<EntrySealedCommitted>, Arc<EntrySealedCommitted>), IdentifyUserResponse> {
149 let Some(ident_entry) = ident.get_user_entry() else {
150 return Err(IdentifyUserResponse::IdentityVerificationUnavailable);
151 };
152
153 let Ok(target_entry) = self.get_partner_entry(target) else {
154 return Err(IdentifyUserResponse::IdentityVerificationUnavailable);
155 };
156
157 if ident_entry.get_uuid() == target_entry.get_uuid() {
158 return Err(IdentifyUserResponse::IdentityVerificationUnavailable);
159 }
160
161 if target_entry
162 .get_ava_single_eckey_public(Attribute::IdVerificationEcKey)
163 .is_none()
164 {
165 return Err(IdentifyUserResponse::IdentityVerificationUnavailable);
166 }
167
168 if ident_entry
169 .get_ava_single_eckey_private(Attribute::IdVerificationEcKey)
170 .is_none()
171 {
172 return Err(IdentifyUserResponse::IdentityVerificationUnavailable);
173 }
174
175 Ok((ident_entry, target_entry))
176 }
177
178 fn get_partner_entry(
179 &mut self,
180 target: Uuid,
181 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
182 self.qs_read
183 .internal_search_uuid(target)
184 .inspect_err(|err| error!(?err, ?target, "Failed to retrieve entry",))
185 }
186
187 fn get_user_own_key(
188 &mut self,
189 ident_entry: &EntrySealedCommitted,
190 ) -> Result<EcKey<Private>, OperationError> {
191 ident_entry
192 .get_ava_single_eckey_private(Attribute::IdVerificationEcKey)
193 .cloned()
194 .ok_or(OperationError::AU0001InvalidState)
195 }
196
197 fn get_user_public_key(
198 &mut self,
199 target_entry: &EntrySealedCommitted,
200 ) -> Result<EcKey<Public>, OperationError> {
201 target_entry
202 .get_ava_single_eckey_public(Attribute::IdVerificationEcKey)
203 .cloned()
204 .ok_or(OperationError::AU0001InvalidState)
205 }
206
207 fn get_self_totp(
208 &mut self,
209 ident_entry: &EntrySealedCommitted,
210 target_entry: &EntrySealedCommitted,
211 ) -> Result<Totp, OperationError> {
212 let self_private = self.get_user_own_key(ident_entry)?;
213 let other_user_public_key = self.get_user_public_key(target_entry)?;
214 let mut shared_key = self.derive_shared_key(self_private, other_user_public_key)?;
215 shared_key.extend_from_slice(ident_entry.get_uuid().as_bytes());
216 let totp = Totp::new(shared_key, TOTP_STEP, TotpAlgo::Sha256, TotpDigits::Six);
217 Ok(totp)
218 }
219
220 fn get_user_totp(
221 &mut self,
222 ident_entry: &EntrySealedCommitted,
223 target_entry: &EntrySealedCommitted,
224 ) -> Result<Totp, OperationError> {
225 let self_private = self.get_user_own_key(ident_entry)?;
226 let other_user_public_key = self.get_user_public_key(target_entry)?;
227 let mut shared_key = self.derive_shared_key(self_private, other_user_public_key)?;
228 shared_key.extend_from_slice(target_entry.get_uuid().as_bytes());
229 let totp = Totp::new(shared_key, TOTP_STEP, TotpAlgo::Sha256, TotpDigits::Six);
230 Ok(totp)
231 }
232
233 fn derive_shared_key(
234 &self,
235 private: EcKey<Private>,
236 public: EcKey<Public>,
237 ) -> Result<Vec<u8>, OperationError> {
238 let cryptography_error = |_| OperationError::CryptographyError;
239 let pkey_private = PKey::from_ec_key(private).map_err(cryptography_error)?;
240 let pkey_public = PKey::from_ec_key(public).map_err(cryptography_error)?;
241
242 let mut private_key_ctx: PkeyCtx<Private> =
243 PkeyCtx::new(&pkey_private).map_err(cryptography_error)?;
244 private_key_ctx.derive_init().map_err(cryptography_error)?;
245 private_key_ctx
246 .derive_set_peer(&pkey_public)
247 .map_err(cryptography_error)?;
248 let keylen = private_key_ctx.derive(None).map_err(cryptography_error)?;
249 let mut tmp_vec = vec![0; keylen];
250 let buffer = tmp_vec.as_mut_slice();
251 private_key_ctx
252 .derive(Some(buffer))
253 .map_err(cryptography_error)?;
254 Ok(buffer.to_vec())
255 }
256}
257
258#[cfg(test)]
259mod test {
260 use kanidm_proto::internal::IdentifyUserResponse;
261
262 use crate::idm::identityverification::{
263 IdentifyUserDisplayCodeEvent, IdentifyUserStartEvent, IdentifyUserSubmitCodeEvent,
264 };
265 use crate::prelude::*;
266
267 #[idm_test]
268 async fn test_identity_verification_unavailable(
269 idms: &IdmServer,
270 _idms_delayed: &IdmServerDelayed,
271 ) {
272 let ct = duration_from_epoch_now();
273 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
274
275 let invalid_user_uuid = Uuid::new_v4();
276 let valid_user_uuid = Uuid::new_v4();
277
278 let e1 = create_invalid_user_account(invalid_user_uuid);
279
280 let e2 = create_valid_user_account(valid_user_uuid);
281
282 let ce = CreateEvent::new_internal(vec![e1, e2]);
283 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
284 assert!(idms_prox_write.commit().is_ok());
285
286 let mut idms_prox_read = idms.proxy_read().await.unwrap();
287
288 let ident = idms_prox_read
289 .qs_read
290 .internal_search_uuid(invalid_user_uuid)
291 .map(Identity::from_impersonate_entry_readonly)
292 .expect("Failed to impersonate identity");
293
294 let res = idms_prox_read.handle_identify_user_start(
295 &IdentifyUserStartEvent::new(invalid_user_uuid, ident.clone()),
296 ct,
297 );
298
299 assert_eq!(
300 res,
301 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
302 );
303
304 let res = idms_prox_read.handle_identify_user_start(
305 &IdentifyUserStartEvent::new(valid_user_uuid, ident.clone()),
306 ct,
307 );
308
309 assert_eq!(
310 res,
311 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
312 );
313
314 let res = idms_prox_read.handle_identify_user_display_code(
315 &IdentifyUserDisplayCodeEvent::new(valid_user_uuid, ident.clone()),
316 ct,
317 );
318
319 assert_eq!(
320 res,
321 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
322 );
323
324 let res = idms_prox_read.handle_identify_user_submit_code(
325 &IdentifyUserSubmitCodeEvent::new(valid_user_uuid, ident, 123456),
326 ct,
327 );
328
329 assert_eq!(
330 res,
331 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
332 );
333 }
334
335 #[idm_test]
336 async fn test_invalid_user_id(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
337 let ct = duration_from_epoch_now();
338 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
339
340 let invalid_user_uuid = Uuid::new_v4();
341 let valid_user_a_uuid = Uuid::new_v4();
342 let valid_user_b_uuid = Uuid::new_v4();
343
344 let e1 = create_invalid_user_account(invalid_user_uuid);
345
346 let e2 = create_valid_user_account(valid_user_a_uuid);
347
348 let e3 = create_valid_user_account(valid_user_b_uuid);
349
350 let ce = CreateEvent::new_internal(vec![e1, e2, e3]);
351
352 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
353 assert!(idms_prox_write.commit().is_ok());
354
355 let mut idms_prox_read = idms.proxy_read().await.unwrap();
356
357 let ident = idms_prox_read
358 .qs_read
359 .internal_search_uuid(valid_user_a_uuid)
360 .map(Identity::from_impersonate_entry_readonly)
361 .expect("Failed to impersonate identity");
362
363 let res = idms_prox_read.handle_identify_user_start(
364 &IdentifyUserStartEvent::new(invalid_user_uuid, ident.clone()),
365 ct,
366 );
367
368 assert!(matches!(
369 res,
370 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
371 ));
372
373 let res = idms_prox_read.handle_identify_user_start(
374 &IdentifyUserStartEvent::new(invalid_user_uuid, ident.clone()),
375 ct,
376 );
377
378 assert!(matches!(
379 res,
380 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
381 ));
382
383 let res = idms_prox_read.handle_identify_user_display_code(
384 &IdentifyUserDisplayCodeEvent::new(invalid_user_uuid, ident.clone()),
385 ct,
386 );
387
388 assert!(matches!(
389 res,
390 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
391 ));
392 let res = idms_prox_read.handle_identify_user_submit_code(
393 &IdentifyUserSubmitCodeEvent::new(invalid_user_uuid, ident, 123456),
394 ct,
395 );
396
397 assert!(matches!(
398 res,
399 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
400 ));
401 }
402
403 #[idm_test]
404 async fn test_start_event(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
405 let ct = duration_from_epoch_now();
406 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
407
408 let valid_user_a_uuid = Uuid::new_v4();
409
410 let e = create_valid_user_account(valid_user_a_uuid);
411 let ce = CreateEvent::new_internal(vec![e]);
412 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
413 assert!(idms_prox_write.commit().is_ok());
414
415 let mut idms_prox_read = idms.proxy_read().await.unwrap();
416
417 let ident = idms_prox_read
418 .qs_read
419 .internal_search_uuid(valid_user_a_uuid)
420 .map(Identity::from_impersonate_entry_readonly)
421 .expect("Failed to impersonate identity");
422
423 let res = idms_prox_read.handle_identify_user_start(
424 &IdentifyUserStartEvent::new(valid_user_a_uuid, ident.clone()),
425 ct,
426 );
427
428 assert!(matches!(
429 res,
430 Ok(IdentifyUserResponse::IdentityVerificationUnavailable)
431 ));
432 }
433
434 #[idm_test] async fn test_code_correctness(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
438 let ct = duration_from_epoch_now();
439 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
440 let user_a_uuid = Uuid::new_v4();
441 let user_b_uuid = Uuid::new_v4();
442 let e1 = create_valid_user_account(user_a_uuid);
443 let e2 = create_valid_user_account(user_b_uuid);
444 let ce = CreateEvent::new_internal(vec![e1, e2]);
445
446 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
447 assert!(idms_prox_write.commit().is_ok());
448
449 let mut idms_prox_read = idms.proxy_read().await.unwrap();
450
451 let ident_a = idms_prox_read
452 .qs_read
453 .internal_search_uuid(user_a_uuid)
454 .map(Identity::from_impersonate_entry_readonly)
455 .expect("Failed to impersonate identity");
456
457 let ident_b = idms_prox_read
458 .qs_read
459 .internal_search_uuid(user_b_uuid)
460 .map(Identity::from_impersonate_entry_readonly)
461 .expect("Failed to impersonate identity");
462
463 let (lower_user, lower_user_uuid, higher_user, higher_user_uuid) =
464 if user_a_uuid < user_b_uuid {
465 (ident_a, user_a_uuid, ident_b, user_b_uuid)
466 } else {
467 (ident_b, user_b_uuid, ident_a, user_a_uuid)
468 };
469
470 let res_higher_user = idms_prox_read.handle_identify_user_start(
473 &IdentifyUserStartEvent::new(lower_user_uuid, higher_user.clone()),
474 ct,
475 );
476
477 let Ok(IdentifyUserResponse::ProvideCode { totp, .. }) = res_higher_user else {
478 panic!();
479 };
480
481 let res_lower_user_wrong = idms_prox_read.handle_identify_user_submit_code(
482 &IdentifyUserSubmitCodeEvent::new(higher_user_uuid, lower_user.clone(), totp + 1),
483 ct,
484 );
485
486 assert!(matches!(
487 res_lower_user_wrong,
488 Ok(IdentifyUserResponse::CodeFailure)
489 ));
490
491 let res_lower_user_correct = idms_prox_read.handle_identify_user_submit_code(
492 &IdentifyUserSubmitCodeEvent::new(higher_user_uuid, lower_user.clone(), totp),
493 ct,
494 );
495
496 assert!(matches!(
497 res_lower_user_correct,
498 Ok(IdentifyUserResponse::ProvideCode { .. })
499 ));
500
501 let Ok(IdentifyUserResponse::ProvideCode { totp, .. }) = res_lower_user_correct else {
504 panic!("Invalid");
505 };
506
507 let res_higher_user_2_wrong = idms_prox_read.handle_identify_user_submit_code(
508 &IdentifyUserSubmitCodeEvent::new(lower_user_uuid, higher_user.clone(), totp + 1),
509 ct,
510 );
511
512 assert!(matches!(
513 res_higher_user_2_wrong,
514 Ok(IdentifyUserResponse::CodeFailure)
515 ));
516
517 let res_higher_user_2_correct = idms_prox_read.handle_identify_user_submit_code(
518 &IdentifyUserSubmitCodeEvent::new(lower_user_uuid, higher_user.clone(), totp),
519 ct,
520 );
521
522 assert!(matches!(
523 res_higher_user_2_correct,
524 Ok(IdentifyUserResponse::Success)
525 ));
526 }
527
528 #[idm_test]
529 async fn test_totps_differ(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
530 let ct = duration_from_epoch_now();
531 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
532 let user_a_uuid = Uuid::new_v4();
533 let user_b_uuid = Uuid::new_v4();
534 let e1 = create_valid_user_account(user_a_uuid);
535 let e2 = create_valid_user_account(user_b_uuid);
536 let ce = CreateEvent::new_internal(vec![e1, e2]);
537
538 assert!(idms_prox_write.qs_write.create(&ce).is_ok());
539 assert!(idms_prox_write.commit().is_ok());
540
541 let mut idms_prox_read = idms.proxy_read().await.unwrap();
542
543 let ident_a = idms_prox_read
544 .qs_read
545 .internal_search_uuid(user_a_uuid)
546 .map(Identity::from_impersonate_entry_readonly)
547 .expect("Failed to impersonate identity");
548
549 let ident_b = idms_prox_read
550 .qs_read
551 .internal_search_uuid(user_b_uuid)
552 .map(Identity::from_impersonate_entry_readonly)
553 .expect("Failed to impersonate identity");
554
555 let (lower_user, lower_user_uuid, higher_user, higher_user_uuid) =
556 if user_a_uuid < user_b_uuid {
557 (ident_a, user_a_uuid, ident_b, user_b_uuid)
558 } else {
559 (ident_b, user_b_uuid, ident_a, user_a_uuid)
560 };
561
562 let res_higher_user = idms_prox_read.handle_identify_user_start(
565 &IdentifyUserStartEvent::new(lower_user_uuid, higher_user.clone()),
566 ct,
567 );
568
569 let Ok(IdentifyUserResponse::ProvideCode {
570 totp: higher_user_totp,
571 ..
572 }) = res_higher_user
573 else {
574 panic!();
575 };
576
577 let res_lower_user_correct = idms_prox_read.handle_identify_user_submit_code(
580 &IdentifyUserSubmitCodeEvent::new(
581 higher_user_uuid,
582 lower_user.clone(),
583 higher_user_totp,
584 ),
585 ct,
586 );
587
588 if let Ok(IdentifyUserResponse::ProvideCode {
589 totp: lower_user_totp,
590 ..
591 }) = res_lower_user_correct
592 {
593 assert_ne!(higher_user_totp, lower_user_totp);
594 } else {
595 debug_assert!(false);
596 }
597 }
598
599 fn create_valid_user_account(uuid: Uuid) -> EntryInitNew {
600 let mut name = String::from("valid_user");
601 name.push_str(&uuid.to_string());
602 name.truncate(14);
605 entry_init!(
606 (Attribute::Class, EntryClass::Object.to_value()),
607 (Attribute::Class, EntryClass::Account.to_value()),
608 (Attribute::Class, EntryClass::Person.to_value()),
609 (Attribute::Name, Value::new_iname(&name)),
610 (Attribute::Uuid, Value::Uuid(uuid)),
611 (Attribute::Description, Value::new_utf8s("some valid user")),
612 (Attribute::DisplayName, Value::new_utf8s("Some valid user"))
613 )
614 }
615
616 fn create_invalid_user_account(uuid: Uuid) -> EntryInitNew {
617 entry_init!(
618 (Attribute::Class, EntryClass::Object.to_value()),
619 (Attribute::Class, EntryClass::Account.to_value()),
620 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
621 (Attribute::Name, Value::new_iname("invalid_user")),
622 (Attribute::Uuid, Value::Uuid(uuid)),
623 (Attribute::Description, Value::new_utf8s("invalid_user")),
624 (Attribute::DisplayName, Value::new_utf8s("Invalid user"))
625 )
626 }
627}