1use crate::client::reqwest_generic::{get, post};
2use crate::config::SmartIDConfig;
3use crate::error::Result;
4use crate::error::SmartIdClientError;
5use crate::error::SmartIdClientError::NoSessionException;
6use crate::models::api::authentication_session::{
7 AuthenticationDeviceLinkRequest, AuthenticationDeviceLinkResponse,
8 AuthenticationNotificationRequest, AuthenticationNotificationResponse,
9};
10use crate::models::api::certificate_choice_session::{CertificateChoiceDeviceLinkRequest, CertificateChoiceDeviceLinkResponse, CertificateChoiceNotificationRequest, CertificateChoiceNotificationResponse, SigningCertificate, SigningCertificateRequest, SigningCertificateResponse, SigningCertificateResponseState};
11use crate::models::api::session_status::{
12 SessionCertificate, SessionResponse, SessionState, SessionStatusResponse,
13};
14use crate::models::api::signature_session::{
15 SignatureDeviceLinkRequest, SignatureDeviceLinkResponse, SignatureNotificationLinkedRequest,
16 SignatureNotificationLinkedResponse, SignatureNotificationRequest,
17 SignatureNotificationResponse,
18};
19use crate::models::common::{SessionConfig, VCCode};
20use crate::models::device_link::DeviceLink::{CrossDeviceLink, SameDeviceLink};
21use crate::models::device_link::{DeviceLinkType, SessionType};
22use crate::models::signature::FlowType;
23use crate::models::user_identity::UserIdentity;
24use crate::utils::demo_certificates::{demo_intermediate_certificates, demo_root_certificates};
25use crate::utils::production_certificates::{
26 production_intermediate_certificates, production_root_certificates,
27};
28use crate::utils::sec_x509::verify_certificate;
29use std::sync::{Arc, Mutex};
30use tracing::debug;
31
32const SESSION_STATUS_URI: &str = "/session";
35const NOTIFICATION_CERTIFICATE_CHOICE_WITH_SEMANTIC_IDENTIFIER_PATH: &str = "/signature/certificate-choice/notification/etsi";
36#[allow(dead_code)]
37const NOTIFICATION_CERTIFICATE_CHOICE_WITH_DOCUMENT_NUMBER_PATH: &str = "/signature/certificate-choice/notification/document";
38const ANONYMOUSE_DEVICE_LINK_CERTIFICATE_CHOICE_PATH: &str = "/signature/certificate-choice/device-link/anonymous";
39const SIGNING_CERTIFICATE_WITH_DOCUMENT_NUMBER_PATH: &str = "/signature/certificate";
40
41const DEVICE_LINK_SIGNATURE_WITH_SEMANTIC_IDENTIFIER_PATH: &str = "/signature/device-link/etsi";
42const DEVICE_LINK_SIGNATURE_WITH_DOCUMENT_NUMBER_PATH: &str = "/signature/device-link/document";
43const NOTIFICATION_SIGNATURE_WITH_SEMANTIC_IDENTIFIER_PATH: &str = "/signature/notification/etsi";
44const NOTIFICATION_SIGNATURE_WITH_DOCUMENT_NUMBER_PATH: &str = "/signature/notification/document";
45const NOTIFICATION_SIGNATURE_WITH_DOCUMENT_NUMBER_LINKED_PATH: &str = "/signature/notification/linked";
46
47const ANONYMOUS_DEVICE_LINK_AUTHENTICATION_PATH: &str = "/authentication/device-link/anonymous";
48const DEVICE_LINK_AUTHENTICATION_WITH_SEMANTIC_IDENTIFIER_PATH: &str =
49 "/authentication/device-link/etsi";
50const DEVICE_LINK_AUTHENTICATION_WITH_DOCUMENT_NUMBER_PATH: &str =
51 "/authentication/device-link/document";
52#[allow(dead_code)]
53const NOTIFICATION_AUTHENTICATION_WITH_SEMANTIC_IDENTIFIER_PATH: &str =
54 "/authentication/notification/etsi";
55#[allow(dead_code)]
56const NOTIFICATION_AUTHENTICATION_WITH_DOCUMENT_NUMBER_PATH: &str =
57 "/authentication/notification/document";
58
59const DEVICE_LINK_VERSION: &str = "1.0";
61
62#[derive(Debug)]
73pub struct SmartIdClient {
74 pub cfg: SmartIDConfig,
75 pub(crate) session_config: Arc<Mutex<Option<SessionConfig>>>,
78 pub(crate) authenticated_identity: Arc<Mutex<Option<UserIdentity>>>,
80 pub(crate) root_certificates: Vec<String>,
83 pub(crate) intermediate_certificates: Vec<String>,
86}
87
88impl SmartIdClient {
89 pub fn new(
102 cfg: &SmartIDConfig,
103 user_identity: Option<UserIdentity>,
104 root_certificates: Vec<String>,
105 intermediate_certificates: Vec<String>,
106 ) -> Self {
107 SmartIdClient {
108 cfg: cfg.clone(),
109 session_config: Arc::new(Mutex::new(None)),
110 authenticated_identity: Arc::new(Mutex::new(user_identity)),
111 root_certificates,
112 intermediate_certificates,
113 }
114 }
115
116 pub fn from_session(
137 cfg: &SmartIDConfig,
138 session_config: SessionConfig,
139 user_identity: Option<UserIdentity>,
140 root_certificates: Vec<String>,
141 intermediate_certificates: Vec<String>,
142 ) -> Self {
143 SmartIdClient {
144 cfg: cfg.clone(),
145 session_config: Arc::new(Mutex::new(Some(session_config))),
146 authenticated_identity: Arc::new(Mutex::new(user_identity)),
147 root_certificates,
148 intermediate_certificates,
149 }
150 }
151
152 pub async fn get_session_status(&self) -> Result<SessionStatusResponse> {
177 let session_config = self.get_session()?;
178
179 let path = format!(
180 "{}{}/{}?timeoutMs={}",
181 self.cfg.api_url(),
182 SESSION_STATUS_URI,
183 session_config.session_id(),
184 self.cfg.long_polling_timeout,
185 );
186
187 let session_response =
188 get::<SessionResponse>(path.as_str(), Some(self.cfg.long_polling_timeout + 100))
189 .await?; let session_status = session_response.into_result()?;
192
193 match session_status.state {
194 SessionState::COMPLETE => {
195 self.validate_session_status(session_status.clone(), session_config)?;
196 Ok(session_status)
197 }
198 SessionState::RUNNING => {
199 Err(SmartIdClientError::StatusRequestLongPollingTimeoutException)
200 }
201 }
202 }
203
204 pub async fn start_authentication_device_link_anonymous_session(
220 &self,
221 authentication_request: AuthenticationDeviceLinkRequest,
222 ) -> Result<()> {
223 self.clear_session();
224
225 let path = format!(
226 "{}{}",
227 self.cfg.api_url(),
228 ANONYMOUS_DEVICE_LINK_AUTHENTICATION_PATH,
229 );
230
231 let authentication_response =
232 post::<AuthenticationDeviceLinkRequest, AuthenticationDeviceLinkResponse>(
233 path.as_str(),
234 &authentication_request,
235 self.cfg.client_request_timeout,
236 )
237 .await?;
238
239 let session = authentication_response.into_result()?;
240
241 self.set_session(SessionConfig::from_authentication_device_link_response(
242 session,
243 authentication_request,
244 &self.cfg.scheme_name,
245 )?)
246 }
247
248 pub async fn start_authentication_device_link_document_session(
261 &self,
262 authentication_request: AuthenticationDeviceLinkRequest,
263 document_number: String,
264 ) -> Result<()> {
265 self.clear_session();
266
267 let path = format!(
268 "{}{}/{}",
269 self.cfg.api_url(),
270 DEVICE_LINK_AUTHENTICATION_WITH_DOCUMENT_NUMBER_PATH,
271 document_number,
272 );
273
274 let authentication_response =
275 post::<AuthenticationDeviceLinkRequest, AuthenticationDeviceLinkResponse>(
276 path.as_str(),
277 &authentication_request,
278 self.cfg.client_request_timeout,
279 )
280 .await?;
281
282 let session = authentication_response.into_result()?;
283
284 self.set_session(SessionConfig::from_authentication_device_link_response(
285 session,
286 authentication_request,
287 &self.cfg.scheme_name,
288 )?)
289 }
290
291 pub async fn start_authentication_device_link_etsi_session(
304 &self,
305 authentication_request: AuthenticationDeviceLinkRequest,
306 etsi: String,
307 ) -> Result<()> {
308 self.clear_session();
309
310 let path = format!(
311 "{}{}/{}",
312 self.cfg.api_url(),
313 DEVICE_LINK_AUTHENTICATION_WITH_SEMANTIC_IDENTIFIER_PATH,
314 etsi,
315 );
316
317 let authentication_response =
318 post::<AuthenticationDeviceLinkRequest, AuthenticationDeviceLinkResponse>(
319 path.as_str(),
320 &authentication_request,
321 self.cfg.client_request_timeout,
322 )
323 .await?;
324
325 let session = authentication_response.into_result()?;
326
327 self.set_session(SessionConfig::from_authentication_device_link_response(
328 session,
329 authentication_request,
330 &self.cfg.scheme_name,
331 )?)
332 }
333
334 pub async fn start_authentication_notification_etsi_session(
346 &self,
347 authentication_request: AuthenticationNotificationRequest,
348 etsi: String,
349 ) -> Result<VCCode> {
350 self.clear_session();
351
352 let path = format!(
353 "{}{}/{}",
354 self.cfg.api_url(),
355 NOTIFICATION_AUTHENTICATION_WITH_SEMANTIC_IDENTIFIER_PATH,
356 etsi,
357 );
358
359 let authentication_response =
360 post::<AuthenticationNotificationRequest, AuthenticationNotificationResponse>(
361 path.as_str(),
362 &authentication_request,
363 self.cfg.client_request_timeout,
364 )
365 .await?;
366
367 let session = authentication_response.into_result()?;
368
369 let session_config = SessionConfig::from_authentication_notification_response(
370 session.clone(),
371 authentication_request,
372 &self.cfg.scheme_name,
373 )?;
374
375 let vc_code = session_config.calculate_vc_code();
376
377 self.set_session(session_config)?;
378
379 vc_code
380 }
381
382 pub async fn start_authentication_notification_document_session(
394 &self,
395 authentication_request: AuthenticationNotificationRequest,
396 document_number: String,
397 ) -> Result<VCCode> {
398 self.clear_session();
399
400 let path = format!(
401 "{}{}/{}",
402 self.cfg.api_url(),
403 NOTIFICATION_AUTHENTICATION_WITH_DOCUMENT_NUMBER_PATH,
404 document_number,
405 );
406
407 let authentication_response =
408 post::<AuthenticationNotificationRequest, AuthenticationNotificationResponse>(
409 path.as_str(),
410 &authentication_request,
411 self.cfg.client_request_timeout,
412 )
413 .await?;
414
415 let session = authentication_response.into_result()?;
416
417 let session_config = SessionConfig::from_authentication_notification_response(
418 session.clone(),
419 authentication_request,
420 &self.cfg.scheme_name,
421 )?;
422
423 let vc_code = session_config.calculate_vc_code();
424
425 self.set_session(session_config)?;
426
427 vc_code
428 }
429
430 pub async fn start_signature_device_link_etsi_session(
447 &self,
448 signature_request: SignatureDeviceLinkRequest,
449 etsi: String,
450 ) -> Result<()> {
451 self.clear_session();
452
453 let path = format!(
454 "{}{}/{}",
455 self.cfg.api_url(),
456 DEVICE_LINK_SIGNATURE_WITH_SEMANTIC_IDENTIFIER_PATH,
457 etsi,
458 );
459
460 let signature_response = post::<SignatureDeviceLinkRequest, SignatureDeviceLinkResponse>(
461 path.as_str(),
462 &signature_request,
463 self.cfg.client_request_timeout,
464 )
465 .await?;
466
467 let session = signature_response.into_result()?;
468
469 self.set_session(SessionConfig::from_signature_device_link_request_response(
470 session,
471 signature_request,
472 &self.cfg.scheme_name,
473 )?)
474 }
475
476 pub async fn start_signature_device_link_document_session(
489 &self,
490 signature_request: SignatureDeviceLinkRequest,
491 document_number: String,
492 ) -> Result<()> {
493 self.clear_session();
494
495 let path = format!(
496 "{}{}/{}",
497 self.cfg.api_url(),
498 DEVICE_LINK_SIGNATURE_WITH_DOCUMENT_NUMBER_PATH,
499 document_number,
500 );
501
502 let signature_response = post::<SignatureDeviceLinkRequest, SignatureDeviceLinkResponse>(
503 path.as_str(),
504 &signature_request,
505 self.cfg.client_request_timeout,
506 )
507 .await?;
508
509 let session = signature_response.into_result()?;
510
511 self.set_session(SessionConfig::from_signature_device_link_request_response(
512 session,
513 signature_request,
514 &self.cfg.scheme_name,
515 )?)
516 }
517
518 pub async fn start_signature_notification_etsi_session(
530 &self,
531 signature_request: SignatureNotificationRequest,
532 etsi: String,
533 ) -> Result<VCCode> {
534 self.clear_session();
535
536 let path = format!(
537 "{}{}/{}",
538 self.cfg.api_url(),
539 NOTIFICATION_SIGNATURE_WITH_SEMANTIC_IDENTIFIER_PATH,
540 etsi,
541 );
542
543 let signature_response =
544 post::<SignatureNotificationRequest, SignatureNotificationResponse>(
545 path.as_str(),
546 &signature_request,
547 self.cfg.client_request_timeout,
548 )
549 .await?;
550
551 let session = signature_response.into_result()?;
552
553 self.set_session(SessionConfig::from_signature_notification_response(
554 session.clone(),
555 signature_request,
556 &self.cfg.scheme_name,
557 )?)?;
558
559 Ok(session.vc)
560 }
561
562 pub async fn start_signature_notification_document_session(
574 &self,
575 signature_request: SignatureNotificationRequest,
576 document_number: String,
577 ) -> Result<VCCode> {
578 self.clear_session();
579
580 let path = format!(
581 "{}{}/{}",
582 self.cfg.api_url(),
583 NOTIFICATION_SIGNATURE_WITH_DOCUMENT_NUMBER_PATH,
584 document_number,
585 );
586
587 let signature_response =
588 post::<SignatureNotificationRequest, SignatureNotificationResponse>(
589 path.as_str(),
590 &signature_request,
591 self.cfg.client_request_timeout,
592 )
593 .await?;
594
595 let session = signature_response.into_result()?;
596
597 self.set_session(SessionConfig::from_signature_notification_response(
598 session.clone(),
599 signature_request,
600 &self.cfg.scheme_name,
601 )?)?;
602
603 Ok(session.vc)
604 }
605
606 pub async fn start_signature_notification_document_linked_session(
618 &self,
619 signature_request: SignatureNotificationLinkedRequest,
620 document_number: String,
621 ) -> Result<()> {
622 self.clear_session();
623
624 let path = format!(
625 "{}{}/{}",
626 self.cfg.api_url(),
627 NOTIFICATION_SIGNATURE_WITH_DOCUMENT_NUMBER_LINKED_PATH,
628 document_number,
629 );
630
631 let signature_response =
632 post::<SignatureNotificationLinkedRequest, SignatureNotificationLinkedResponse>(
633 path.as_str(),
634 &signature_request,
635 self.cfg.client_request_timeout,
636 )
637 .await?;
638
639 let session = signature_response.into_result()?;
640
641 self.set_session(SessionConfig::from_signature_notification_linked_response(
642 session.clone(),
643 signature_request,
644 &self.cfg.scheme_name,
645 )?)?;
646
647 Ok(())
648 }
649
650 pub async fn start_certificate_choice_notification_etsi_session(
666 &self,
667 certificate_choice_request: CertificateChoiceNotificationRequest,
668 etsi: String,
669 ) -> Result<()> {
670 self.clear_session();
671
672 let path = format!(
673 "{}{}/{}",
674 self.cfg.api_url(),
675 NOTIFICATION_CERTIFICATE_CHOICE_WITH_SEMANTIC_IDENTIFIER_PATH,
676 etsi,
677 );
678
679 let certificate_choice_response =
680 post::<CertificateChoiceNotificationRequest, CertificateChoiceNotificationResponse>(
681 path.as_str(),
682 &certificate_choice_request,
683 self.cfg.client_request_timeout,
684 )
685 .await?;
686
687 let session = certificate_choice_response.into_result()?;
688
689 self.set_session(
690 SessionConfig::from_certificate_choice_notification_response(
691 session,
692 certificate_choice_request,
693 &self.cfg.scheme_name,
694 ),
695 )
696 }
697
698 pub async fn start_certificate_choice_anonymous_session(
710 &self,
711 certificate_choice_request: CertificateChoiceDeviceLinkRequest,
712 ) -> Result<()> {
713 self.clear_session();
714
715 let path = format!(
716 "{}{}",
717 self.cfg.api_url(),
718 ANONYMOUSE_DEVICE_LINK_CERTIFICATE_CHOICE_PATH,
719 );
720
721 let certificate_choice_response =
722 post::<CertificateChoiceDeviceLinkRequest, CertificateChoiceDeviceLinkResponse>(
723 path.as_str(),
724 &certificate_choice_request,
725 self.cfg.client_request_timeout,
726 )
727 .await?;
728
729 let session = certificate_choice_response.into_result()?;
730
731 self.set_session(
732 SessionConfig::from_certificate_choice_device_link_response(
733 session,
734 certificate_choice_request,
735 &self.cfg.scheme_name,
736 ),
737 )
738 }
739
740 pub async fn get_signing_certificate(
751 &self,
752 document_number: String,
753 signing_certificate_request: SigningCertificateRequest,
754 ) -> Result<SigningCertificate> {
755 self.clear_session();
756 let path = format!(
757 "{}{}/{}",
758 self.cfg.api_url(),
759 SIGNING_CERTIFICATE_WITH_DOCUMENT_NUMBER_PATH,
760 document_number,
761 );
762
763 let certificate_choice_response =
764 post::<SigningCertificateRequest, SigningCertificateResponse>(
765 path.as_str(),
766 &signing_certificate_request,
767 self.cfg.client_request_timeout,
768 )
769 .await?;
770
771 match certificate_choice_response {
772 SigningCertificateResponse::Success(signing_certificate_result) => {
773 match signing_certificate_result.state {
774 SigningCertificateResponseState::OK => Ok(signing_certificate_result.cert),
775 SigningCertificateResponseState::DOCUMENT_UNUSABLE => {
776 Err(SmartIdClientError::GetSigningCertificateException(
777 "Document is unusable".to_string(),
778 ))
779 }
780 }
781 }
782 SigningCertificateResponse::Error(e) => Err(
783 SmartIdClientError::GetSigningCertificateException(e.error_type),
784 ),
785 }
786 }
787
788 pub fn generate_device_link(
811 &self,
812 device_link_type: DeviceLinkType,
813 language_code: &str,
814 ) -> Result<String> {
815 let session: SessionConfig = self.get_session()?;
816
817 match session {
818 SessionConfig::AuthenticationDeviceLink {
819 session_secret,
820 session_token,
821 device_link_base,
822 relying_party_name,
823 initial_callback_url,
824 signature_protocol,
825 interactions,
826 rp_challenge,
827 session_start_time,
828 ..
829 } => {
830 let device_link = match device_link_type {
831 DeviceLinkType::Web2App | DeviceLinkType::App2App => {
832 if let Some(initial_callback_url) = initial_callback_url {
833 SameDeviceLink {
834 device_link_base,
835 device_link_type,
836 session_token,
837 session_type: SessionType::auth,
838 version: DEVICE_LINK_VERSION.to_string(),
839 language_code: language_code.to_string(),
840 session_secret,
841 scheme_name: self.cfg.scheme_name.clone(),
842 signature_protocol: Some(signature_protocol),
843 rp_challenge_or_digest: rp_challenge,
844 relying_party_name,
845 brokered_rp_name: "".to_string(),
846 interactions,
847 initial_callback_url,
848 }
849 } else {
850 return Err(SmartIdClientError::GenerateDeviceLinkException(
851 "Initial callback URL is required for Web2App or App2App device links",
852 ));
853 }
854 }
855 DeviceLinkType::QR => {
856 CrossDeviceLink {
857 device_link_base,
858 device_link_type,
859 session_start_time,
860 session_token,
861 session_type: SessionType::auth,
862 version: DEVICE_LINK_VERSION.to_string(),
863 language_code: language_code.to_string(),
864 session_secret,
865 scheme_name: self.cfg.scheme_name.clone(),
866 signature_protocol: Some(signature_protocol),
867 rp_challenge_or_digest: rp_challenge,
868 relying_party_name,
869 brokered_rp_name: "".to_string(),
870 interactions,
871 initial_callback_url,
872 }
873 }
874 };
875 Ok(device_link.generate_device_link())
876 }
877 SessionConfig::SignatureDeviceLink {
878 session_secret,
879 session_token,
880 device_link_base,
881 relying_party_name,
882 initial_callback_url,
883 signature_protocol,
884 interactions,
885 digest,
886 session_start_time,
887 ..
888 } => {
889 let device_link = match device_link_type {
890 DeviceLinkType::Web2App | DeviceLinkType::App2App => {
891 if let Some(initial_callback_url) = initial_callback_url {
892 SameDeviceLink {
893 device_link_base,
894 device_link_type,
895 session_token,
896 session_type: SessionType::sign,
897 version: DEVICE_LINK_VERSION.to_string(),
898 language_code: language_code.to_string(),
899 session_secret,
900 scheme_name: self.cfg.scheme_name.clone(),
901 signature_protocol: Some(signature_protocol),
902 rp_challenge_or_digest: digest,
903 relying_party_name,
904 brokered_rp_name: "".to_string(),
905 interactions,
906 initial_callback_url,
907 }
908 } else {
909 return Err(SmartIdClientError::GenerateDeviceLinkException(
910 "Initial callback URL is required for Web2App or App2App device links",
911 ));
912 }
913 }
914 DeviceLinkType::QR => {
915 CrossDeviceLink {
916 device_link_base,
917 device_link_type,
918 session_start_time,
919 session_token,
920 session_type: SessionType::sign,
921 version: DEVICE_LINK_VERSION.to_string(),
922 language_code: language_code.to_string(),
923 session_secret,
924 scheme_name: self.cfg.scheme_name.clone(),
925 signature_protocol: Some(signature_protocol),
926 rp_challenge_or_digest: digest,
927 relying_party_name,
928 brokered_rp_name: "".to_string(),
929 interactions,
930 initial_callback_url,
931 }
932 }
933 };
934 Ok(device_link.generate_device_link())
935 }
936 SessionConfig::CertificateChoiceDeviceLink {
937 session_token,
938 session_secret,
939 device_link_base,
940 relying_party_name,
941 initial_callback_url,
942 session_start_time,
943 ..
944 } => {
945 let device_link = match device_link_type {
946 DeviceLinkType::Web2App | DeviceLinkType::App2App => {
947 if let Some(initial_callback_url) = initial_callback_url {
948 SameDeviceLink {
949 device_link_base,
950 device_link_type,
951 session_token,
952 session_type: SessionType::cert,
953 version: DEVICE_LINK_VERSION.to_string(),
954 language_code: language_code.to_string(),
955 session_secret,
956 scheme_name: self.cfg.scheme_name.clone(),
957 signature_protocol: None,
958 rp_challenge_or_digest: "".to_string(),
959 relying_party_name,
960 brokered_rp_name: "".to_string(),
961 interactions: "".to_string(),
962 initial_callback_url,
963 }
964 } else {
965 return Err(SmartIdClientError::GenerateDeviceLinkException(
966 "Initial callback URL is required for Web2App or App2App device links",
967 ));
968 }
969 }
970 DeviceLinkType::QR => {
971 CrossDeviceLink {
972 device_link_base,
973 device_link_type,
974 session_start_time,
975 session_token,
976 session_type: SessionType::cert,
977 version: DEVICE_LINK_VERSION.to_string(),
978 language_code: language_code.to_string(),
979 session_secret,
980 scheme_name: self.cfg.scheme_name.clone(),
981 signature_protocol: None,
982 rp_challenge_or_digest: "".to_string(),
983 relying_party_name,
984 brokered_rp_name: "".to_string(),
985 interactions: "".to_string(),
986 initial_callback_url,
987 }
988 }
989 };
990 Ok(device_link.generate_device_link())
991 }
992 _ => {
993 Err(SmartIdClientError::GenerateDeviceLinkException(
994 "Can only generate device links for authentication or signature device link sessions",
995 ))
996 }
997
998 }
999 }
1000
1001 fn validate_session_status(
1038 &self,
1039 session_status: SessionStatusResponse,
1040 session_config: SessionConfig,
1041 ) -> Result<()> {
1042 match session_status.result.clone() {
1043 Some(session_result) => {
1044 session_result.end_result.is_ok()?;
1046
1047 let cert = session_status
1049 .cert
1050 .clone()
1051 .ok_or(SmartIdClientError::SessionResponseMissingCertificate)?;
1052
1053 self.verify_certificate(cert.value.clone())?;
1055
1056 if &cert.certificate_level < session_config.requested_certificate_level() {
1058 Err(
1059 SmartIdClientError::FailedToValidateSessionResponseCertificate(format!(
1060 "Certificate level is not high enough: {:?} < {:?}",
1061 cert.certificate_level,
1062 session_config.requested_certificate_level()
1063 )),
1064 )?
1065 };
1066
1067 self.validate_signature(session_config, session_status, cert.clone())?;
1069
1070 if let Some(user_identity) = self.get_user_identity()? {
1072 user_identity.identity_matches_certificate(cert.value)?
1073 }
1074
1075 Ok(())
1076 }
1077 None => match session_status.state {
1078 SessionState::RUNNING => Ok(()),
1079 SessionState::COMPLETE => {
1080 Err(SmartIdClientError::AuthenticationSessionCompletedWithoutResult)
1081 }
1082 },
1083 }
1084 }
1085
1086 pub fn verify_certificate(&self, cert: String) -> Result<Vec<String>> {
1097 if self.cfg.is_demo() {
1098 let mut root_certs = demo_root_certificates();
1099 root_certs.extend(self.root_certificates.clone());
1100 let mut intermediate_certs = demo_intermediate_certificates();
1101 intermediate_certs.extend(self.intermediate_certificates.clone());
1102 verify_certificate(&cert, intermediate_certs, root_certs)
1103 } else {
1104 let mut root_certs = production_root_certificates();
1105 root_certs.extend(self.root_certificates.clone());
1106 let mut intermediate_certs = production_intermediate_certificates();
1107 intermediate_certs.extend(self.intermediate_certificates.clone());
1108 verify_certificate(
1109 &cert,
1110 production_root_certificates(),
1111 production_intermediate_certificates(),
1112 )
1113 }
1114 }
1115
1116 fn validate_signature(
1117 &self,
1118 session_config: SessionConfig,
1119 session_status_response: SessionStatusResponse,
1120 cert: SessionCertificate,
1121 ) -> Result<()> {
1122 match session_config {
1123 SessionConfig::AuthenticationDeviceLink {
1124 relying_party_name,
1125 initial_callback_url,
1126 interactions,
1127 rp_challenge,
1128 scheme_name,
1129 signature_protocol,
1130 signature_protocol_parameters,
1131 ..
1132 } => {
1133 let signature = session_status_response
1134 .signature
1135 .ok_or(SmartIdClientError::SessionResponseMissingSignature)?;
1136
1137 if signature.get_flow_type() == FlowType::App2App
1138 || signature.get_flow_type() == FlowType::Web2App
1139 {
1140 debug!("When the user goes to the callback a secret is appended to the URL, this is needed to verify the signature for these flows");
1141 return Ok(());
1142 }
1143
1144 let used_interaction_type = session_status_response
1145 .interaction_type_used
1146 .ok_or(SmartIdClientError::SessionResponseMissingInteractionType)?;
1147
1148 signature.validate_acsp_v2(
1149 scheme_name,
1150 signature_protocol,
1151 rp_challenge,
1152 cert.value.clone(),
1153 relying_party_name,
1154 None,
1155 interactions,
1156 used_interaction_type,
1157 initial_callback_url,
1158 signature_protocol_parameters.get_hashing_algorithm(),
1159 )?;
1160
1161 if self.get_user_identity()?.is_none() {
1164 self.set_user_identity(UserIdentity::from_certificate(cert.value.clone())?)?
1165 };
1166
1167 Ok(())
1168 }
1169 SessionConfig::AuthenticationNotification {
1170 relying_party_name,
1171 interactions,
1172 rp_challenge,
1173 scheme_name,
1174 signature_protocol,
1175 signature_protocol_parameters,
1176 ..
1177 } => {
1178 let signature = session_status_response
1179 .signature
1180 .ok_or(SmartIdClientError::SessionResponseMissingSignature)?;
1181
1182 let used_interaction_type = session_status_response
1183 .interaction_type_used
1184 .ok_or(SmartIdClientError::SessionResponseMissingInteractionType)?;
1185
1186 signature.validate_acsp_v2(
1187 scheme_name,
1188 signature_protocol,
1189 rp_challenge,
1190 cert.value.clone(),
1191 relying_party_name,
1192 None,
1193 interactions,
1194 used_interaction_type,
1195 None,
1196 signature_protocol_parameters.get_hashing_algorithm(),
1197 )?;
1198
1199 if self.get_user_identity()?.is_none() {
1202 self.set_user_identity(UserIdentity::from_certificate(cert.value.clone())?)?
1203 };
1204
1205 Ok(())
1206 }
1207 SessionConfig::SignatureDeviceLink {
1208 digest,
1209 signature_protocol_parameters,
1210 ..
1211 } => {
1212 let signature = session_status_response
1213 .signature
1214 .ok_or(SmartIdClientError::SessionResponseMissingSignature)?;
1215
1216 let parameters = signature
1217 .get_signature_algorithm_parameters()
1218 .ok_or(SmartIdClientError::SessionResponseMissingSignature)?;
1219
1220 if self.cfg.is_demo() {
1222 return Ok(());
1223 }
1224
1225 signature.validate_raw_digest(
1226 digest,
1227 cert.value.clone(),
1228 signature_protocol_parameters.get_hashing_algorithm(),
1229 parameters.salt_length,
1230 )
1231 }
1232 SessionConfig::SignatureNotification {
1233 digest,
1234 signature_protocol_parameters,
1235 ..
1236 } => {
1237 let signature = session_status_response
1238 .signature
1239 .ok_or(SmartIdClientError::SessionResponseMissingSignature)?;
1240
1241 let parameters = signature
1242 .get_signature_algorithm_parameters()
1243 .ok_or(SmartIdClientError::SessionResponseMissingSignature)?;
1244
1245 if self.cfg.is_demo() {
1247 return Ok(());
1248 }
1249
1250 signature.validate_raw_digest(
1251 digest,
1252 cert.value.clone(),
1253 signature_protocol_parameters.get_hashing_algorithm(),
1254 parameters.salt_length,
1255 )
1256 }
1257 _ => {
1258 debug!("Signature validation only needed for device link authentication and signature sessions");
1259 Ok(())
1260 }
1261 }
1262 }
1263
1264 pub fn reset_session(&self) {
1272 self.clear_session();
1273 self.clear_user_identity();
1274 }
1275
1276 pub fn get_session(&self) -> Result<SessionConfig> {
1277 match self.session_config.lock() {
1278 Ok(guard) => match guard.clone() {
1279 Some(s) => Ok(s),
1280 None => {
1281 debug!("Can't get session there is no running session");
1282 Err(NoSessionException)
1283 }
1284 },
1285 Err(e) => {
1286 debug!("Failed to lock session config: {:?}", e);
1287 Err(SmartIdClientError::GetSessionException)
1288 }
1289 }
1290 }
1291
1292 fn set_session(&self, session: SessionConfig) -> Result<()> {
1293 match self.session_config.lock() {
1294 Ok(mut guard) => {
1295 *guard = Some(session);
1296 Ok(())
1297 }
1298 Err(e) => {
1299 debug!("Failed to lock session config: {:?}", e);
1300 Err(SmartIdClientError::SetSessionException)
1301 }
1302 }
1303 }
1304
1305 fn clear_session(&self) {
1306 match self.session_config.lock() {
1307 Ok(mut guard) => {
1308 *guard = None;
1309 }
1310 Err(e) => {
1311 debug!("Failed to lock session config: {:?}", e);
1312 }
1313 }
1314 }
1315
1316 pub fn get_user_identity(&self) -> Result<Option<UserIdentity>> {
1317 match self.authenticated_identity.lock() {
1318 Ok(guard) => match guard.clone() {
1319 Some(s) => Ok(Some(s)),
1320 None => Ok(None),
1321 },
1322 Err(e) => {
1323 debug!("Failed to lock authenticated identity: {:?}", e);
1324 Err(SmartIdClientError::GetUserIdentityException)
1325 }
1326 }
1327 }
1328
1329 fn set_user_identity(&self, user_identity: UserIdentity) -> Result<()> {
1330 match self.authenticated_identity.lock() {
1331 Ok(mut guard) => {
1332 *guard = Some(user_identity);
1333 Ok(())
1334 }
1335 Err(e) => {
1336 debug!("Failed to lock authenticated identity: {:?}", e);
1337 Err(SmartIdClientError::SetUserIdentityException)
1338 }
1339 }
1340 }
1341
1342 #[allow(dead_code)]
1343 fn clear_user_identity(&self) {
1344 match self.authenticated_identity.lock() {
1345 Ok(mut guard) => {
1346 *guard = None;
1347 }
1348 Err(e) => {
1349 debug!("Failed to lock authenticated identity: {:?}", e);
1350 }
1351 }
1352 }
1353
1354 }