1use super::global_config::GlobalConfigBuilder;
8use crate::authenticators::Authenticate;
9use crate::back::{
10 backend_handler::{BackEndHandler, BackEndHandlerBuilder},
11 dispatcher::DispatcherBuilder,
12};
13use crate::front::{
14 domain_socket::DomainSocketListenerBuilder, front_end::FrontEndHandler,
15 front_end::FrontEndHandlerBuilder, listener::Listen,
16};
17use crate::key_info_managers::KeyInfoManagerFactory;
18use crate::providers::{core::ProviderBuilder as CoreProviderBuilder, Provide};
19use crate::utils::config::{
20 AuthenticatorConfig, KeyInfoManagerConfig, ListenerConfig, ListenerType, ProviderConfig,
21 ServiceConfig,
22};
23use anyhow::Result;
24use log::{error, warn};
25use parsec_interface::operations_protobuf::ProtobufConverter;
26use parsec_interface::requests::AuthType;
27use parsec_interface::requests::{BodyType, ProviderId};
28use std::collections::HashMap;
29use std::collections::HashSet;
30use std::io::{Error, ErrorKind};
31use std::sync::Arc;
32use std::time::Duration;
33use threadpool::{Builder as ThreadPoolBuilder, ThreadPool};
34
35#[cfg(feature = "direct-authenticator")]
36use crate::authenticators::direct_authenticator::DirectAuthenticator;
37#[cfg(feature = "jwt-svid-authenticator")]
38use crate::authenticators::jwt_svid_authenticator::JwtSvidAuthenticator;
39#[cfg(feature = "unix-peer-credentials-authenticator")]
40use crate::authenticators::unix_peer_credentials_authenticator::UnixPeerCredentialsAuthenticator;
41
42#[cfg(feature = "cryptoauthlib-provider")]
43use crate::providers::cryptoauthlib::ProviderBuilder as CryptoAuthLibProviderBuilder;
44#[cfg(feature = "mbed-crypto-provider")]
45use crate::providers::mbed_crypto::ProviderBuilder as MbedCryptoProviderBuilder;
46#[cfg(feature = "pkcs11-provider")]
47use crate::providers::pkcs11::ProviderBuilder as Pkcs11ProviderBuilder;
48#[cfg(feature = "tpm-provider")]
49use crate::providers::tpm::ProviderBuilder as TpmProviderBuilder;
50#[cfg(feature = "trusted-service-provider")]
51use crate::providers::trusted_service::ProviderBuilder as TrustedServiceProviderBuilder;
52
53#[cfg(feature = "cryptoauthlib-provider")]
54use crate::providers::cryptoauthlib::Provider as CryptoAuthLibProvider;
55#[cfg(feature = "mbed-crypto-provider")]
56use crate::providers::mbed_crypto::Provider as MbedCryptoProvider;
57#[cfg(feature = "pkcs11-provider")]
58use crate::providers::pkcs11::Provider as Pkcs11Provider;
59#[cfg(feature = "tpm-provider")]
60use crate::providers::tpm::Provider as TpmProvider;
61#[cfg(feature = "trusted-service-provider")]
62use crate::providers::trusted_service::Provider as TrustedServiceProvider;
63
64#[cfg(any(
65 feature = "mbed-crypto-provider",
66 feature = "pkcs11-provider",
67 feature = "tpm-provider",
68 feature = "cryptoauthlib-provider",
69 feature = "trusted-service-provider"
70))]
71use crate::providers::ProviderIdentity;
72
73#[cfg(any(
74 feature = "mbed-crypto-provider",
75 feature = "pkcs11-provider",
76 feature = "tpm-provider",
77 feature = "cryptoauthlib-provider",
78 feature = "trusted-service-provider"
79))]
80use log::info;
81
82const WIRE_PROTOCOL_VERSION_MINOR: u8 = 0;
83const WIRE_PROTOCOL_VERSION_MAJOR: u8 = 1;
84
85const DEFAULT_BODY_LEN_LIMIT: usize = 1 << 20;
87
88pub const DEFAULT_BUFFER_SIZE_LIMIT: usize = 1 << 20;
90
91type Provider = Arc<dyn Provide + Send + Sync>;
92type Authenticator = Box<dyn Authenticate + Send + Sync>;
93
94#[derive(Copy, Clone, Debug)]
101pub struct ServiceBuilder;
102
103impl ServiceBuilder {
104 pub fn build_service(config: &ServiceConfig) -> Result<FrontEndHandler> {
112 GlobalConfigBuilder::new()
113 .with_log_error_details(config.core_settings.log_error_details.unwrap_or(false))
114 .with_buffer_size_limit(
115 config
116 .core_settings
117 .buffer_size_limit
118 .unwrap_or(DEFAULT_BUFFER_SIZE_LIMIT),
119 )
120 .with_allow_deprecated(config.core_settings.allow_deprecated.unwrap_or(false))
121 .build();
122
123 let authenticators = build_authenticators(&config.authenticator)?;
124
125 if authenticators[0].0 == AuthType::Direct {
126 warn!("Direct authenticator has been set as the default one. It is only secure under specific requirements. Please make sure to read the Recommendations on a Secure Parsec Deployment at https://parallaxsecond.github.io/parsec-book/parsec_security/secure_deployment.html");
127 }
128
129 let key_info_manager_builders = get_key_info_manager_builders(
130 config.key_manager.as_ref().unwrap_or(&Vec::new()),
131 authenticators[0].0,
132 )?;
133
134 let providers = build_providers(
135 config.provider.as_ref().unwrap_or(&Vec::new()),
136 key_info_manager_builders,
137 )?;
138
139 if providers.is_empty() {
140 error!("Parsec needs at least one provider to start. No valid provider could be created from the configuration.");
141 return Err(Error::new(ErrorKind::InvalidData, "need one provider").into());
142 }
143
144 let backend_handlers = build_backend_handlers(providers, &authenticators)?;
145
146 let dispatcher = DispatcherBuilder::new()
147 .with_backends(backend_handlers)
148 .build()?;
149
150 let mut front_end_handler_builder = FrontEndHandlerBuilder::new();
151 for (auth_type, authenticator) in authenticators {
152 front_end_handler_builder =
153 front_end_handler_builder.with_authenticator(auth_type, authenticator);
154 }
155 front_end_handler_builder = front_end_handler_builder
156 .with_dispatcher(dispatcher)
157 .with_body_len_limit(
158 config
159 .core_settings
160 .body_len_limit
161 .unwrap_or(DEFAULT_BODY_LEN_LIMIT),
162 );
163
164 Ok(front_end_handler_builder.build()?)
165 }
166
167 pub fn start_listener(config: ListenerConfig) -> Result<Box<dyn Listen>> {
169 let listener = match config.listener_type {
170 ListenerType::DomainSocket => DomainSocketListenerBuilder::new()
171 .with_timeout(Duration::from_millis(config.timeout))
172 .with_socket_path(config.socket_path.map(|s| s.into()))
173 .build(),
174 }?;
175
176 Ok(Box::new(listener))
177 }
178
179 pub fn build_threadpool(num_threads: Option<usize>) -> ThreadPool {
181 let mut threadpool_builder = ThreadPoolBuilder::new();
182 if let Some(num_threads) = num_threads {
183 threadpool_builder = threadpool_builder.num_threads(num_threads);
184 }
185 threadpool_builder.build()
186 }
187}
188
189fn build_backend_handlers(
190 mut providers: Vec<(ProviderId, Provider)>,
191 authenticators: &[(AuthType, Authenticator)],
192) -> Result<HashMap<ProviderId, BackEndHandler>> {
193 let mut map = HashMap::new();
194
195 let mut core_provider_builder = CoreProviderBuilder::new()
196 .with_wire_protocol_version(WIRE_PROTOCOL_VERSION_MINOR, WIRE_PROTOCOL_VERSION_MAJOR);
197
198 for (_auth_type, authenticator) in authenticators {
199 let authenticator_info = authenticator
200 .describe()
201 .map_err(|_| Error::new(ErrorKind::Other, "Failed to describe authenticator"))?;
202 core_provider_builder = core_provider_builder.with_authenticator_info(authenticator_info);
203 }
204
205 for (provider_id, provider) in providers.drain(..) {
206 core_provider_builder = core_provider_builder.with_provider(provider.clone());
207
208 let backend_handler = BackEndHandlerBuilder::new()
209 .with_provider(provider)
210 .with_converter(Box::from(ProtobufConverter {}))
211 .with_provider_id(provider_id)
212 .with_content_type(BodyType::Protobuf)
213 .with_accept_type(BodyType::Protobuf)
214 .build()?;
215 let _ = map.insert(provider_id, backend_handler);
216 }
217
218 let core_provider_backend = BackEndHandlerBuilder::new()
219 .with_provider(Arc::new(core_provider_builder.build()?))
220 .with_converter(Box::from(ProtobufConverter {}))
221 .with_provider_id(ProviderId::Core)
222 .with_content_type(BodyType::Protobuf)
223 .with_accept_type(BodyType::Protobuf)
224 .build()?;
225
226 let _ = map.insert(ProviderId::Core, core_provider_backend);
227
228 Ok(map)
229}
230
231fn build_providers(
232 configs: &[ProviderConfig],
233 kim_factorys: HashMap<String, KeyInfoManagerFactory>,
234) -> Result<Vec<(ProviderId, Provider)>> {
235 let mut providers = Vec::new();
236 let mut provider_names = HashSet::new();
237 for config in configs {
238 let provider_id = config.provider_id();
240
241 let provider_name = config.provider_name()?;
243 if provider_names.contains(&provider_name) {
244 error!("Duplicate provider names found.\n{} was found twice.\nThe \'[[provider]] name config option can be used to differentiate between providers of the same type.\nPlease check your config.toml file.", provider_name);
245 return Err(
246 Error::new(ErrorKind::InvalidData, "duplicate provider names found").into(),
247 );
248 }
249 let _ = provider_names.insert(provider_name.clone());
250
251 let kim_factory = match kim_factorys.get(config.key_info_manager()) {
252 Some(kim_factory) => kim_factory,
253 None => {
254 format_error!(
255 "Key info manager builder with specified name was not found",
256 config.key_info_manager()
257 );
258 return Err(Error::new(
259 ErrorKind::InvalidData,
260 "key info manager builder not found",
261 )
262 .into());
263 }
264 };
265 let provider = match unsafe { get_provider(config, kim_factory) } {
267 Ok(None) => {
268 warn!("Provider {} is skipped.", provider_id);
269 continue;
270 }
271 Ok(Some(provider)) => provider,
272 Err(e) => {
273 format_error!(
274 &format!("Provider with ID {} cannot be created", provider_id),
275 e
276 );
277 return Err(Error::new(ErrorKind::Other, "failed to create provider").into());
278 }
279 };
280 providers.push((provider_id, provider));
281 }
282
283 Ok(providers)
284}
285
286#[cfg_attr(
289 not(all(
290 feature = "mbed-crypto-provider",
291 feature = "pkcs11-provider",
292 feature = "tpm-provider",
293 feature = "cryptoauthlib-provider",
294 feature = "trusted-service-provider"
295 )),
296 allow(unused_variables),
297 allow(clippy::match_single_binding)
298)]
299unsafe fn get_provider(
301 config: &ProviderConfig,
302 kim_factory: &KeyInfoManagerFactory,
303) -> Result<Option<Provider>> {
304 match config {
305 #[cfg(feature = "mbed-crypto-provider")]
306 ProviderConfig::MbedCrypto { .. } => {
307 info!("Creating a Mbed Crypto Provider.");
308 let provider_identity = ProviderIdentity::new(
309 MbedCryptoProvider::PROVIDER_UUID.to_string(),
310 config.provider_name()?,
311 );
312 Ok(Some(Arc::new(
313 MbedCryptoProviderBuilder::new()
314 .with_key_info_store(kim_factory.build_client(provider_identity))
315 .with_provider_name(config.provider_name()?)
316 .build()?,
317 )))
318 }
319 #[cfg(feature = "pkcs11-provider")]
320 ProviderConfig::Pkcs11 {
321 library_path,
322 slot_number,
323 serial_number,
324 user_pin,
325 software_public_operations,
326 allow_export,
327 ..
328 } => {
329 info!("Creating a PKCS 11 Provider.");
330 let provider_identity = ProviderIdentity::new(
331 Pkcs11Provider::PROVIDER_UUID.to_string(),
332 config.provider_name()?,
333 );
334 Ok(Some(Arc::new(
335 Pkcs11ProviderBuilder::new()
336 .with_key_info_store(kim_factory.build_client(provider_identity))
337 .with_provider_name(config.provider_name()?)
338 .with_pkcs11_library_path(library_path.clone())
339 .with_slot_number(*slot_number)
340 .with_serial_number(serial_number.clone())
341 .with_user_pin(user_pin.clone())
342 .with_software_public_operations(*software_public_operations)
343 .with_allow_export(*allow_export)
344 .build()?,
345 )))
346 }
347 #[cfg(feature = "tpm-provider")]
348 ProviderConfig::Tpm {
349 tcti,
350 owner_hierarchy_auth,
351 endorsement_hierarchy_auth,
352 skip_if_no_tpm,
353 ..
354 } => {
355 use std::str::FromStr;
356 use tss_esapi::tcti_ldr::{TctiContext, TctiNameConf};
357 info!("Creating a TPM Provider.");
358
359 let provider_identity = ProviderIdentity::new(
360 TpmProvider::PROVIDER_UUID.to_string(),
361 config.provider_name()?,
362 );
363
364 let tcti_name_conf = TctiNameConf::from_str(tcti).map_err(|_| {
365 Error::new(ErrorKind::InvalidData, "Invalid TCTI configuration string")
366 })?;
367 if *skip_if_no_tpm == Some(true) {
368 let _tcti_context = match TctiContext::initialize(tcti_name_conf) {
371 Ok(tcti_context) => tcti_context,
372 Err(e) => {
373 format_error!("Error creating a TCTI context", e);
374 return Ok(None);
378 }
379 };
380 }
381
382 let mut builder = TpmProviderBuilder::new()
383 .with_key_info_store(kim_factory.build_client(provider_identity))
384 .with_tcti(tcti)
385 .with_provider_name(config.provider_name()?)
386 .with_owner_hierarchy_auth(owner_hierarchy_auth.clone());
387 if endorsement_hierarchy_auth.is_some() {
388 builder = builder.with_endorsement_hierarchy_auth(
389 endorsement_hierarchy_auth.as_ref().unwrap().clone(),
390 );
391 }
392 Ok(Some(Arc::new(builder.build()?)))
393 }
394 #[cfg(feature = "cryptoauthlib-provider")]
395 ProviderConfig::CryptoAuthLib {
396 device_type,
397 iface_type,
398 wake_delay,
399 rx_retries,
400 slave_address,
401 bus,
402 baud,
403 access_key_file_name,
404 ..
405 } => {
406 info!("Creating a CryptoAuthentication Library Provider.");
407 let provider_identity = ProviderIdentity::new(
408 CryptoAuthLibProvider::PROVIDER_UUID.to_string(),
409 config.provider_name()?,
410 );
411 Ok(Some(Arc::new(
412 CryptoAuthLibProviderBuilder::new()
413 .with_key_info_store(kim_factory.build_client(provider_identity))
414 .with_provider_name(config.provider_name()?)
415 .with_device_type(device_type.to_string())
416 .with_iface_type(iface_type.to_string())
417 .with_wake_delay(*wake_delay)
418 .with_rx_retries(*rx_retries)
419 .with_slave_address(*slave_address)
420 .with_bus(*bus)
421 .with_baud(*baud)
422 .with_access_key_file(access_key_file_name.clone())
423 .build()?,
424 )))
425 }
426 #[cfg(feature = "trusted-service-provider")]
427 ProviderConfig::TrustedService { .. } => {
428 info!("Creating a Trusted Service Provider.");
429 let provider_identity = ProviderIdentity::new(
430 TrustedServiceProvider::PROVIDER_UUID.to_string(),
431 config.provider_name()?,
432 );
433 Ok(Some(Arc::new(
434 TrustedServiceProviderBuilder::new()
435 .with_key_info_store(kim_factory.build_client(provider_identity))
436 .with_provider_name(config.provider_name()?)
437 .build()?,
438 )))
439 }
440 #[cfg(not(all(
441 feature = "mbed-crypto-provider",
442 feature = "pkcs11-provider",
443 feature = "tpm-provider",
444 feature = "cryptoauthlib-provider",
445 feature = "trusted-service-provider"
446 )))]
447 _ => {
448 error!(
449 "Provider \"{:?}\" chosen in the configuration was not compiled in Parsec binary.",
450 config
451 );
452 Err(Error::new(ErrorKind::InvalidData, "provider not compiled").into())
453 }
454 }
455}
456
457fn get_key_info_manager_builders(
458 configs: &[KeyInfoManagerConfig],
459 default_auth_type: AuthType,
460) -> Result<HashMap<String, KeyInfoManagerFactory>> {
461 let mut map = HashMap::new();
462 for config in configs {
463 let _ = map.insert(
464 config.name.clone(),
465 KeyInfoManagerFactory::new(config, default_auth_type)?,
466 );
467 }
468
469 Ok(map)
470}
471
472#[allow(clippy::unnecessary_wraps)]
474fn build_authenticators(config: &AuthenticatorConfig) -> Result<Vec<(AuthType, Authenticator)>> {
475 let mut authenticators: Vec<(AuthType, Authenticator)> = Vec::new();
481
482 match config {
483 #[cfg(feature = "direct-authenticator")]
484 AuthenticatorConfig::Direct { admins } => authenticators.push((
485 AuthType::Direct,
486 Box::from(DirectAuthenticator::new(
487 admins.as_ref().cloned().unwrap_or_default(),
488 )),
489 )),
490 #[cfg(feature = "unix-peer-credentials-authenticator")]
491 AuthenticatorConfig::UnixPeerCredentials { admins } => authenticators.push((
492 AuthType::UnixPeerCredentials,
493 Box::from(UnixPeerCredentialsAuthenticator::new(
494 admins.as_ref().cloned().unwrap_or_default(),
495 )),
496 )),
497 #[cfg(feature = "jwt-svid-authenticator")]
498 AuthenticatorConfig::JwtSvid {
499 workload_endpoint,
500 admins,
501 } => {
502 let jwt_svid_authenticator = match JwtSvidAuthenticator::new(
503 workload_endpoint.to_string(),
504 admins.as_ref().cloned().unwrap_or_default(),
505 ) {
506 Some(authenticator) => authenticator,
507 None => {
508 return Err(Error::new(
509 ErrorKind::Other,
510 "can not create a SPIFFE Workload API client",
511 )
512 .into())
513 }
514 };
515 authenticators.push((AuthType::JwtSvid, Box::from(jwt_svid_authenticator)))
516 }
517 #[cfg(not(all(
518 feature = "direct-authenticator",
519 feature = "unix-peer-credentials-authenticator",
520 feature = "jwt-svid-authenticator",
521 )))]
522 _ => {
523 error!(
524 "Authenticator \"{:?}\" chosen in the configuration was not compiled in Parsec binary.",
525 config
526 );
527 return Err(Error::new(ErrorKind::InvalidData, "authenticator not compiled").into());
528 }
529 };
530
531 Ok(authenticators)
532}