1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::net::IpAddr; 15 use std::path::Path; 16 use std::sync::Arc; 17 18 use crate::error::{ErrorKind, HttpClientError}; 19 use crate::util::c_openssl::error::ErrorStack; 20 use crate::util::c_openssl::ssl::{ 21 Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslVersion, 22 }; 23 use crate::util::c_openssl::verify::PubKeyPins; 24 use crate::util::c_openssl::x509::{X509Store, X509}; 25 use crate::util::config::tls::DefaultCertVerifier; 26 use crate::util::AlpnProtocolList; 27 28 /// `TlsContextBuilder` implementation based on `SSL_CTX`. 29 /// 30 /// # Examples 31 /// 32 /// ``` 33 /// use ylong_http_client::{TlsConfigBuilder, TlsVersion}; 34 /// 35 /// let context = TlsConfigBuilder::new() 36 /// .ca_file("ca.crt") 37 /// .max_proto_version(TlsVersion::TLS_1_2) 38 /// .min_proto_version(TlsVersion::TLS_1_2) 39 /// .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") 40 /// .build(); 41 /// ``` 42 pub struct TlsConfigBuilder { 43 inner: Result<SslContextBuilder, ErrorStack>, 44 cert_verifier: Option<Arc<DefaultCertVerifier>>, 45 use_sni: bool, 46 verify_hostname: bool, 47 certs_list: Vec<Cert>, 48 pins: Option<PubKeyPins>, 49 #[cfg(feature = "c_openssl_3_0")] 50 paths_list: Vec<String>, 51 } 52 53 impl TlsConfigBuilder { 54 /// Creates a new, default `SslContextBuilder`. 55 /// 56 /// # Examples 57 /// 58 /// ``` 59 /// use ylong_http_client::TlsConfigBuilder; 60 /// 61 /// let builder = TlsConfigBuilder::new(); 62 /// ``` new() -> Self63 pub fn new() -> Self { 64 Self { 65 inner: SslContext::builder(SslMethod::tls_client()), 66 cert_verifier: None, 67 use_sni: true, 68 verify_hostname: true, 69 certs_list: vec![], 70 pins: None, 71 #[cfg(feature = "c_openssl_3_0")] 72 paths_list: vec![], 73 } 74 } 75 76 /// Loads trusted root certificates from a file. The file should contain a 77 /// sequence of PEM-formatted CA certificates. 78 /// 79 /// # Examples 80 /// 81 /// ``` 82 /// use ylong_http_client::TlsConfigBuilder; 83 /// 84 /// let builder = TlsConfigBuilder::new().ca_file("ca.crt"); 85 /// ``` ca_file<T: AsRef<Path>>(mut self, path: T) -> Self86 pub fn ca_file<T: AsRef<Path>>(mut self, path: T) -> Self { 87 self.inner = self 88 .inner 89 .and_then(|mut builder| builder.set_ca_file(path).map(|_| builder)); 90 self 91 } 92 93 /// Sets the maximum supported protocol version. A value of `None` will 94 /// enable protocol versions down the highest version supported by 95 /// `OpenSSL`. 96 /// 97 /// Requires `OpenSSL 1.1.0` or `LibreSSL 2.6.1` or newer. 98 /// 99 /// # Examples 100 /// 101 /// ``` 102 /// use ylong_http_client::{TlsConfigBuilder, TlsVersion}; 103 /// 104 /// let builder = TlsConfigBuilder::new().max_proto_version(TlsVersion::TLS_1_2); 105 /// ``` max_proto_version(mut self, version: TlsVersion) -> Self106 pub fn max_proto_version(mut self, version: TlsVersion) -> Self { 107 self.inner = self.inner.and_then(|mut builder| { 108 builder 109 .set_max_proto_version(version.into_inner()) 110 .map(|_| builder) 111 }); 112 self 113 } 114 115 /// Sets the minimum supported protocol version. A value of `None` will 116 /// enable protocol versions down the the lowest version supported by 117 /// `OpenSSL`. 118 /// 119 /// Requires `OpenSSL 1.1.0` or `LibreSSL 2.6.1` or newer. 120 /// 121 /// # Examples 122 /// 123 /// ``` 124 /// use ylong_http_client::{TlsConfigBuilder, TlsVersion}; 125 /// 126 /// let builder = TlsConfigBuilder::new().min_proto_version(TlsVersion::TLS_1_2); 127 /// ``` min_proto_version(mut self, version: TlsVersion) -> Self128 pub fn min_proto_version(mut self, version: TlsVersion) -> Self { 129 self.inner = self.inner.and_then(|mut builder| { 130 builder 131 .set_min_proto_version(version.into_inner()) 132 .map(|_| builder) 133 }); 134 self 135 } 136 137 /// Sets the list of supported ciphers for protocols before `TLSv1.3`. 138 /// 139 /// The `set_ciphersuites` method controls the cipher suites for `TLSv1.3`. 140 /// 141 /// See [`ciphers`] for details on the format. 142 /// 143 /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html 144 /// 145 /// # Examples 146 /// 147 /// ``` 148 /// use ylong_http_client::TlsConfigBuilder; 149 /// 150 /// let builder = TlsConfigBuilder::new() 151 /// .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"); 152 /// ``` cipher_list(mut self, list: &str) -> Self153 pub fn cipher_list(mut self, list: &str) -> Self { 154 self.inner = self 155 .inner 156 .and_then(|mut builder| builder.set_cipher_list(list).map(|_| builder)); 157 self 158 } 159 160 /// Sets the list of supported ciphers for the `TLSv1.3` protocol. 161 /// 162 /// The `set_cipher_list` method controls the cipher suites for protocols 163 /// before `TLSv1.3`. 164 /// 165 /// The format consists of TLSv1.3 cipher suite names separated by `:` 166 /// characters in order of preference. 167 /// 168 /// Requires `OpenSSL 1.1.1` or `LibreSSL 3.4.0` or newer. 169 /// 170 /// # Examples 171 /// 172 /// ``` 173 /// use ylong_http_client::TlsConfigBuilder; 174 /// 175 /// let builder = TlsConfigBuilder::new() 176 /// .cipher_suites("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"); 177 /// ``` cipher_suites(mut self, list: &str) -> Self178 pub fn cipher_suites(mut self, list: &str) -> Self { 179 self.inner = self 180 .inner 181 .and_then(|mut builder| builder.set_cipher_suites(list).map(|_| builder)); 182 self 183 } 184 185 /// Loads a leaf certificate from a file. 186 /// 187 /// Only a single certificate will be loaded - use `add_extra_chain_cert` to 188 /// add the remainder of the certificate chain, or 189 /// `set_certificate_chain_file` to load the entire chain from a single 190 /// file. 191 /// 192 /// # Examples 193 /// 194 /// ``` 195 /// use ylong_http_client::{TlsConfigBuilder, TlsFileType}; 196 /// 197 /// let builder = TlsConfigBuilder::new().certificate_file("cert.pem", TlsFileType::PEM); 198 /// ``` certificate_file<T: AsRef<Path>>(mut self, path: T, file_type: TlsFileType) -> Self199 pub fn certificate_file<T: AsRef<Path>>(mut self, path: T, file_type: TlsFileType) -> Self { 200 self.inner = self.inner.and_then(|mut builder| { 201 builder 202 .set_certificate_file(path, file_type.into_inner()) 203 .map(|_| builder) 204 }); 205 self 206 } 207 208 /// Loads a certificate chain from a file. 209 /// 210 /// The file should contain a sequence of PEM-formatted certificates, 211 /// the first being the leaf certificate, and the remainder forming the 212 /// chain of certificates up to and including the trusted root certificate. 213 /// 214 /// # Examples 215 /// 216 /// ``` 217 /// use ylong_http_client::TlsConfigBuilder; 218 /// 219 /// let builder = TlsConfigBuilder::new().certificate_chain_file("cert.pem"); 220 /// ``` certificate_chain_file<T: AsRef<Path>>(mut self, path: T) -> Self221 pub fn certificate_chain_file<T: AsRef<Path>>(mut self, path: T) -> Self { 222 self.inner = self 223 .inner 224 .and_then(|mut builder| builder.set_certificate_chain_file(path).map(|_| builder)); 225 self 226 } 227 228 /// Adds custom root certificate. 229 /// 230 /// # Examples 231 /// 232 /// ``` 233 /// use ylong_http_client::{Cert, TlsConfigBuilder}; 234 /// # fn example(certs: Vec<Cert>) { 235 /// let builder = TlsConfigBuilder::new().add_root_certificates(certs); 236 /// # } 237 /// ``` add_root_certificates(mut self, mut certs: Vec<Cert>) -> Self238 pub fn add_root_certificates(mut self, mut certs: Vec<Cert>) -> Self { 239 self.certs_list.append(&mut certs); 240 self 241 } 242 243 /// Adds custom root certificate. 244 /// 245 /// # Examples 246 /// 247 /// ``` 248 /// use ylong_http_client::TlsConfigBuilder; 249 /// # fn example(path: String) { 250 /// let builder = TlsConfigBuilder::new().add_path_certificates(path); 251 /// # } 252 /// ``` 253 #[cfg(feature = "c_openssl_3_0")] add_path_certificates(mut self, path: String) -> Self254 pub fn add_path_certificates(mut self, path: String) -> Self { 255 self.paths_list.push(path); 256 self 257 } 258 259 // Sets the protocols to sent to the server for Application Layer Protocol 260 // Negotiation (ALPN). 261 // 262 // Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. 263 #[cfg(feature = "http2")] alpn_protos(mut self, protocols: &[u8]) -> Self264 pub(crate) fn alpn_protos(mut self, protocols: &[u8]) -> Self { 265 self.inner = self 266 .inner 267 .and_then(|mut builder| builder.set_alpn_protos(protocols).map(|_| builder)); 268 self 269 } 270 271 // Sets the protocols to sent to the server for Application Layer Protocol 272 // Negotiation (ALPN). 273 // 274 // This method is based on `openssl::SslContextBuilder::set_alpn_protos`. 275 // Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. alpn_proto_list(mut self, list: AlpnProtocolList) -> Self276 pub(crate) fn alpn_proto_list(mut self, list: AlpnProtocolList) -> Self { 277 self.inner = self 278 .inner 279 .and_then(|mut builder| builder.set_alpn_protos(list.as_slice()).map(|_| builder)); 280 self 281 } 282 283 /// Controls the use of built-in system certificates during certificate 284 /// validation. Default to `true` -- uses built-in system certs. build_in_root_certs(mut self, is_use: bool) -> Self285 pub fn build_in_root_certs(mut self, is_use: bool) -> Self { 286 if !is_use { 287 self.inner = X509Store::new().and_then(|store| { 288 self.inner.and_then(|mut builder| { 289 { 290 builder.set_cert_store(store); 291 Ok(()) 292 } 293 .map(|_| builder) 294 }) 295 }); 296 } 297 self 298 } 299 300 /// Controls the use of certificates verification. 301 /// 302 /// Defaults to `false` -- verify certificates. 303 /// 304 /// # Warning 305 /// 306 /// When sets `true`, any certificate for any site will be trusted for use. 307 /// 308 /// # Examples 309 /// 310 /// ``` 311 /// use ylong_http_client::TlsConfigBuilder; 312 /// 313 /// let builder = TlsConfigBuilder::new().danger_accept_invalid_certs(true); 314 /// ``` danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self315 pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self { 316 if is_invalid { 317 self.inner = self.inner.and_then(|mut builder| { 318 { 319 builder.set_verify(crate::util::c_openssl::ssl::SSL_VERIFY_NONE); 320 Ok(()) 321 } 322 .map(|_| builder) 323 }); 324 } 325 self 326 } 327 328 /// Controls the use of hostname verification. 329 /// 330 /// Defaults to `false` -- verify hostname. 331 /// 332 /// # Warning 333 /// 334 /// When sets `true`, any valid certificate for any site will be trusted for 335 /// use from any other. 336 /// 337 /// # Examples 338 /// 339 /// ``` 340 /// use ylong_http_client::TlsConfigBuilder; 341 /// 342 /// let builder = TlsConfigBuilder::new().danger_accept_invalid_hostnames(true); 343 /// ``` danger_accept_invalid_hostnames(mut self, invalid_hostname: bool) -> Self344 pub fn danger_accept_invalid_hostnames(mut self, invalid_hostname: bool) -> Self { 345 self.verify_hostname = !invalid_hostname; 346 self 347 } 348 cert_verifier(mut self, verifier: Arc<DefaultCertVerifier>) -> Self349 pub(crate) fn cert_verifier(mut self, verifier: Arc<DefaultCertVerifier>) -> Self { 350 let inner = Arc::as_ptr(&verifier); 351 self.cert_verifier = Some(verifier); 352 self.inner = self.inner.map(|mut builder| { 353 builder.set_cert_verify_callback(inner); 354 builder 355 }); 356 self 357 } 358 pinning_public_key(mut self, pin: PubKeyPins) -> Self359 pub(crate) fn pinning_public_key(mut self, pin: PubKeyPins) -> Self { 360 self.pins = Some(pin); 361 self 362 } 363 364 /// Controls the use of TLS server name indication. 365 /// 366 /// Defaults to `true` -- sets sni. 367 /// 368 /// # Examples 369 /// 370 /// ``` 371 /// use ylong_http_client::TlsConfigBuilder; 372 /// 373 /// let builder = TlsConfigBuilder::new().sni(true); 374 /// ``` sni(mut self, use_sni: bool) -> Self375 pub fn sni(mut self, use_sni: bool) -> Self { 376 self.use_sni = use_sni; 377 self 378 } 379 380 /// Builds a `TlsContext`. Returns `Err` if an error occurred during 381 /// configuration. 382 /// 383 /// # Examples 384 /// 385 /// ``` 386 /// use ylong_http_client::{TlsConfigBuilder, TlsVersion}; 387 /// 388 /// let context = TlsConfigBuilder::new() 389 /// .ca_file("ca.crt") 390 /// .max_proto_version(TlsVersion::TLS_1_2) 391 /// .min_proto_version(TlsVersion::TLS_1_2) 392 /// .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") 393 /// .build(); 394 /// ``` build(mut self) -> Result<TlsConfig, HttpClientError>395 pub fn build(mut self) -> Result<TlsConfig, HttpClientError> { 396 for cert in self.certs_list { 397 self.inner = self.inner.and_then(|mut builder| { 398 Ok(builder.cert_store_mut()) 399 .and_then(|store| store.add_cert(cert.0)) 400 .map(|_| builder) 401 }); 402 } 403 404 #[cfg(feature = "c_openssl_3_0")] 405 for path in self.paths_list { 406 self.inner = self.inner.and_then(|mut builder| { 407 Ok(builder.cert_store_mut()) 408 .and_then(|store| store.add_path(path)) 409 .map(|_| builder) 410 }); 411 } 412 413 let ctx = self 414 .inner 415 .map(|builder| builder.build()) 416 .map_err(|e| HttpClientError::from_error(ErrorKind::Build, e))?; 417 418 Ok(TlsConfig { 419 ctx, 420 cert_verifier: self.cert_verifier, 421 use_sni: self.use_sni, 422 verify_hostname: self.verify_hostname, 423 pins: self.pins, 424 }) 425 } 426 } 427 428 impl Default for TlsConfigBuilder { default() -> Self429 fn default() -> Self { 430 Self::new() 431 } 432 } 433 434 /// `TlsContext` is based on `SSL_CTX`, which provides context 435 /// object of `TLS` streams. 436 /// 437 /// # Examples 438 /// 439 /// ``` 440 /// use ylong_http_client::TlsConfig; 441 /// 442 /// let builder = TlsConfig::builder(); 443 /// ``` 444 #[derive(Clone)] 445 pub struct TlsConfig { 446 ctx: SslContext, 447 #[allow(dead_code)] 448 cert_verifier: Option<Arc<DefaultCertVerifier>>, 449 use_sni: bool, 450 verify_hostname: bool, 451 pins: Option<PubKeyPins>, 452 } 453 454 impl TlsConfig { 455 /// Creates a new, default `TlsContextBuilder`. 456 /// 457 /// # Examples 458 /// 459 /// ``` 460 /// use ylong_http_client::TlsConfig; 461 /// 462 /// let builder = TlsConfig::builder(); 463 /// ``` builder() -> TlsConfigBuilder464 pub fn builder() -> TlsConfigBuilder { 465 TlsConfigBuilder::new() 466 } 467 468 /// Creates a new, default `TlsSsl`. ssl_new(&self, domain: &str) -> Result<TlsSsl, ErrorStack>469 pub(crate) fn ssl_new(&self, domain: &str) -> Result<TlsSsl, ErrorStack> { 470 let ctx = &self.ctx; 471 let mut ssl = Ssl::new(ctx)?; 472 473 // SNI extension in `ClientHello` stage. 474 if self.use_sni && domain.parse::<IpAddr>().is_err() { 475 ssl.set_host_name_in_sni(domain)?; 476 } 477 478 // Hostname verification in certificate verification. 479 if self.verify_hostname { 480 ssl.set_verify_hostname(domain)?; 481 } 482 Ok(TlsSsl(ssl)) 483 } 484 pinning_host_match(&self, domain: &str) -> Option<String>485 pub(crate) fn pinning_host_match(&self, domain: &str) -> Option<String> { 486 match &self.pins { 487 None => None, 488 Some(pins) => pins.get_pin(domain), 489 } 490 } 491 } 492 493 impl Default for TlsConfig { default() -> Self494 fn default() -> Self { 495 // It certainly can be successful. 496 TlsConfig::builder() 497 .build() 498 .expect("TlsConfig build error!") 499 } 500 } 501 502 /// /// `TlsSsl` is based on `Ssl` 503 pub(crate) struct TlsSsl(Ssl); 504 505 impl TlsSsl { into_inner(self) -> Ssl506 pub(crate) fn into_inner(self) -> Ssl { 507 self.0 508 } 509 } 510 511 /// `TlsVersion` is based on `openssl::SslVersion`, which provides `SSL/TLS` 512 /// protocol version. 513 /// 514 /// # Examples 515 /// 516 /// ``` 517 /// use ylong_http_client::TlsVersion; 518 /// 519 /// let version = TlsVersion::TLS_1_2; 520 /// ``` 521 pub struct TlsVersion(SslVersion); 522 523 impl TlsVersion { 524 /// Constant for TLS version 1. 525 pub const TLS_1_0: Self = Self(SslVersion::TLS_1_0); 526 /// Constant for TLS version 1.1. 527 pub const TLS_1_1: Self = Self(SslVersion::TLS_1_1); 528 /// Constant for TLS version 1.2. 529 pub const TLS_1_2: Self = Self(SslVersion::TLS_1_2); 530 /// Constant for TLS version 1.3. 531 pub const TLS_1_3: Self = Self(SslVersion::TLS_1_3); 532 533 /// Consumes `TlsVersion` and then takes `SslVersion`. into_inner(self) -> SslVersion534 pub(crate) fn into_inner(self) -> SslVersion { 535 self.0 536 } 537 } 538 539 /// `TlsFileType` is based on `openssl::SslFileType`, which provides an 540 /// identifier of the format of a certificate or key file. 541 /// 542 /// ``` 543 /// use ylong_http_client::TlsFileType; 544 /// 545 /// let file_type = TlsFileType::PEM; 546 /// ``` 547 pub struct TlsFileType(SslFiletype); 548 549 impl TlsFileType { 550 /// Constant for PEM file type. 551 pub const PEM: Self = Self(SslFiletype::PEM); 552 /// Constant for ASN1 file type. 553 pub const ASN1: Self = Self(SslFiletype::ASN1); 554 555 /// Consumes `TlsFileType` and then takes `SslFiletype`. into_inner(self) -> SslFiletype556 pub(crate) fn into_inner(self) -> SslFiletype { 557 self.0 558 } 559 } 560 561 /// `Cert` is based on `X509`, which indicates `X509` public 562 /// key certificate. 563 /// 564 /// ``` 565 /// # use ylong_http_client::Cert; 566 /// 567 /// # fn read_from_pem(pem: &[u8]) { 568 /// let cert = Cert::from_pem(pem); 569 /// # } 570 /// 571 /// # fn read_from_der(der: &[u8]) { 572 /// let cert = Cert::from_der(der); 573 /// # } 574 /// ``` 575 #[derive(Clone)] 576 pub struct Cert(X509); 577 578 impl Cert { 579 /// Deserializes a PEM-encoded `Cert` structure. 580 /// 581 /// The input should have a header like below: 582 /// 583 /// ```text 584 /// -----BEGIN CERTIFICATE----- 585 /// ``` 586 /// 587 /// # Examples 588 /// 589 /// ``` 590 /// # use ylong_http_client::Cert; 591 /// 592 /// # fn read_from_pem(pem: &[u8]) { 593 /// let cert = Cert::from_pem(pem); 594 /// # } 595 /// ``` from_pem(pem: &[u8]) -> Result<Self, HttpClientError>596 pub fn from_pem(pem: &[u8]) -> Result<Self, HttpClientError> { 597 Ok(Self(X509::from_pem(pem).map_err(|e| { 598 HttpClientError::from_error(ErrorKind::Build, e) 599 })?)) 600 } 601 602 /// Deserializes a DER-encoded `Cert` structure. 603 /// 604 /// # Examples 605 /// 606 /// ``` 607 /// use ylong_http_client::Cert; 608 /// 609 /// # fn read_from_der(der: &[u8]) { 610 /// let cert = Cert::from_der(der); 611 /// # } 612 /// ``` from_der(der: &[u8]) -> Result<Self, HttpClientError>613 pub fn from_der(der: &[u8]) -> Result<Self, HttpClientError> { 614 Ok(Self(X509::from_der(der).map_err(|e| { 615 HttpClientError::from_error(ErrorKind::Build, e) 616 })?)) 617 } 618 619 /// Deserializes a list of PEM-formatted certificates. stack_from_pem(pem: &[u8]) -> Result<Vec<Self>, HttpClientError>620 pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<Self>, HttpClientError> { 621 Ok(X509::stack_from_pem(pem) 622 .map_err(|e| HttpClientError::from_error(ErrorKind::Build, e))? 623 .into_iter() 624 .map(Self) 625 .collect()) 626 } 627 } 628 629 /// Represents a server X509 certificates. 630 /// 631 /// You can use `from_pem` to parse a `&[u8]` into a list of certificates. 632 /// 633 /// # Examples 634 /// 635 /// ``` 636 /// use ylong_http_client::Certificate; 637 /// 638 /// fn from_pem(pem: &[u8]) { 639 /// let certs = Certificate::from_pem(pem); 640 /// } 641 /// ``` 642 #[derive(Clone)] 643 pub struct Certificate { 644 inner: CertificateList, 645 } 646 647 #[derive(Clone)] 648 pub(crate) enum CertificateList { 649 CertList(Vec<Cert>), 650 #[cfg(feature = "c_openssl_3_0")] 651 PathList(String), 652 } 653 654 impl Certificate { 655 /// Deserializes a list of PEM-formatted certificates. from_pem(pem: &[u8]) -> Result<Self, HttpClientError>656 pub fn from_pem(pem: &[u8]) -> Result<Self, HttpClientError> { 657 let cert_list = X509::stack_from_pem(pem) 658 .map_err(|e| HttpClientError::from_error(ErrorKind::Build, e))? 659 .into_iter() 660 .map(Cert) 661 .collect(); 662 Ok(Certificate { 663 inner: CertificateList::CertList(cert_list), 664 }) 665 } 666 667 /// Deserializes a list of PEM-formatted certificates. 668 #[cfg(feature = "c_openssl_3_0")] from_path(path: &str) -> Result<Self, HttpClientError>669 pub fn from_path(path: &str) -> Result<Self, HttpClientError> { 670 Ok(Certificate { 671 inner: CertificateList::PathList(path.to_string()), 672 }) 673 } 674 into_inner(self) -> CertificateList675 pub(crate) fn into_inner(self) -> CertificateList { 676 self.inner 677 } 678 } 679 680 #[cfg(test)] 681 mod ut_openssl_adapter { 682 use crate::util::c_openssl::adapter::CertificateList; 683 use crate::util::{Cert, TlsConfigBuilder, TlsFileType, TlsVersion}; 684 use crate::{AlpnProtocol, AlpnProtocolList, Certificate}; 685 686 /// UT test cases for `TlsConfigBuilder::new`. 687 /// 688 /// # Brief 689 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new` 690 /// 2. Checks if the result is as expected. 691 #[test] ut_tls_config_builder_new()692 fn ut_tls_config_builder_new() { 693 let _ = TlsConfigBuilder::default(); 694 let builder = TlsConfigBuilder::new(); 695 assert!(builder.ca_file("folder/ca.crt").build().is_err()); 696 } 697 698 /// UT test cases for `TlsConfigBuilder::new`. 699 /// 700 /// # Brief 701 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 702 /// 2. Calls `set_cipher_suites`. 703 /// 3. Provides an invalid path as argument. 704 /// 4. Checks if the result is as expected. 705 #[test] ut_set_cipher_suites()706 fn ut_set_cipher_suites() { 707 let builder = TlsConfigBuilder::new().cipher_suites("INVALID STRING"); 708 assert!(builder.build().is_err()); 709 } 710 711 /// UT test cases for `TlsConfigBuilder::set_max_proto_version`. 712 /// 713 /// # Brief 714 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 715 /// 2. Calls `set_max_proto_version`. 716 /// 3. Checks if the result is as expected. 717 #[test] ut_set_max_proto_version()718 fn ut_set_max_proto_version() { 719 let builder = TlsConfigBuilder::new() 720 .max_proto_version(TlsVersion::TLS_1_2) 721 .build(); 722 assert!(builder.is_ok()); 723 } 724 725 /// UT test cases for `TlsConfigBuilder::set_min_proto_version`. 726 /// 727 /// # Brief 728 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 729 /// 2. Calls `set_min_proto_version`. 730 /// 3. Checks if the result is as expected. 731 #[test] ut_set_min_proto_version()732 fn ut_set_min_proto_version() { 733 let builder = TlsConfigBuilder::new() 734 .min_proto_version(TlsVersion::TLS_1_2) 735 .build(); 736 assert!(builder.is_ok()); 737 } 738 739 /// UT test cases for `TlsConfigBuilder::set_cipher_list`. 740 /// 741 /// # Brief 742 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 743 /// 2. Calls `set_cipher_list`. 744 /// 3. Checks if the result is as expected. 745 #[test] ut_set_cipher_list()746 fn ut_set_cipher_list() { 747 let builder = TlsConfigBuilder::new() 748 .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") 749 .build(); 750 assert!(builder.is_ok()); 751 } 752 753 /// UT test cases for `TlsConfigBuilder::set_certificate_file`. 754 /// 755 /// # Brief 756 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 757 /// 2. Calls `set_certificate_file`. 758 /// 3. Provides an invalid path as argument. 759 /// 4. Checks if the result is as expected. 760 #[test] ut_set_certificate_file()761 fn ut_set_certificate_file() { 762 let builder = TlsConfigBuilder::new() 763 .certificate_file("cert.pem", TlsFileType::PEM) 764 .build(); 765 assert!(builder.is_err()); 766 } 767 768 /// UT test cases for `TlsConfigBuilder::set_certificate_chain_file`. 769 /// 770 /// # Brief 771 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 772 /// 2. Calls `set_certificate_chain_file`. 773 /// 3. Provides an invalid path as argument. 774 /// 4. Checks if the result is as expected. 775 #[test] ut_set_certificate_chain_file()776 fn ut_set_certificate_chain_file() { 777 let builder = TlsConfigBuilder::new() 778 .certificate_chain_file("cert.pem") 779 .build(); 780 assert!(builder.is_err()); 781 } 782 783 /// UT test cases for `TlsConfigBuilder::add_root_certificates`. 784 /// 785 /// # Brief 786 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 787 /// 2. Calls `add_root_certificates`. 788 /// 3. Provides PEM-formatted certificates. 789 /// 4. Checks if the result is as expected. 790 #[test] ut_add_root_certificates()791 fn ut_add_root_certificates() { 792 let certificate = Certificate::from_pem(include_bytes!("../../../tests/file/root-ca.pem")) 793 .expect("Sets certs error."); 794 #[cfg(feature = "c_openssl_1_1")] 795 let CertificateList::CertList(certs) = certificate.inner; 796 #[cfg(feature = "c_openssl_3_0")] 797 let certs = match certificate.inner { 798 CertificateList::CertList(c) => c, 799 CertificateList::PathList(_) => vec![], 800 }; 801 802 let builder = TlsConfigBuilder::new().add_root_certificates(certs).build(); 803 assert!(builder.is_ok()); 804 } 805 806 /// UT test cases for `Certificate::clone`. 807 /// 808 /// # Brief 809 /// 1. Creates a `Certificate` by calling `Certificate::from_pem`. 810 /// 2. Creates another `Certificate` by calling `Certificate::clone`. 811 /// 3. Checks if the result is as expected. 812 #[test] 813 #[allow(clippy::redundant_clone)] ut_certificate_clone()814 fn ut_certificate_clone() { 815 let pem = include_bytes!("../../../tests/file/root-ca.pem"); 816 let certificate = Certificate::from_pem(pem).unwrap(); 817 drop(certificate.clone()); 818 } 819 820 /// UT test cases for `Cert::clone`. 821 /// 822 /// # Brief 823 /// 1. Creates a `Cert` by calling `Cert::from_pem`. 824 /// 2. Creates another `Cert` by calling `Cert::clone`. 825 /// 3. Checks if the result is as expected. 826 #[test] 827 #[allow(clippy::redundant_clone)] ut_cert_clone()828 fn ut_cert_clone() { 829 let pem = include_bytes!("../../../tests/file/root-ca.pem"); 830 let cert = Cert::from_pem(pem).unwrap(); 831 drop(cert.clone()); 832 } 833 834 /// UT test cases for `TlsConfigBuilder::build_in_root_certs`. 835 /// 836 /// # Brief 837 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 838 /// 2. Calls `build_in_root_certs`. 839 /// 3. Checks if the result is as expected. 840 #[test] ut_build_in_root_certs()841 fn ut_build_in_root_certs() { 842 let builder = TlsConfigBuilder::new().build_in_root_certs(true).build(); 843 assert!(builder.is_ok()); 844 } 845 846 /// UT test cases for `TlsConfigBuilder::set_alpn_proto_list`. 847 /// 848 /// # Brief 849 /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`. 850 /// 2. Calls `set_alpn_proto_list`. 851 /// 3. Provides `AlpnProtocol`s. 852 /// 4. Checks if the result is as expected. 853 #[test] ut_set_alpn_proto_list()854 fn ut_set_alpn_proto_list() { 855 let builder = TlsConfigBuilder::new() 856 .alpn_proto_list( 857 AlpnProtocolList::new() 858 .extend(AlpnProtocol::HTTP11) 859 .extend(AlpnProtocol::H2), 860 ) 861 .build(); 862 assert!(builder.is_ok()); 863 } 864 865 /// UT test cases for `TlsConfig::ssl`. 866 /// 867 /// # Brief 868 /// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and 869 /// `TlsConfigBuilder::build`. 870 /// 2. Creates a `TlsSsl` by calling `TlsConfig::ssl_new`. 871 /// 3. Calls `TlsSsl::into_inner`. 872 /// 4. Checks if the result is as expected. 873 #[test] ut_tls_ssl()874 fn ut_tls_ssl() { 875 let config = TlsConfigBuilder::new() 876 .build() 877 .expect("TlsConfig build error."); 878 let _ssl = config 879 .ssl_new("host name") 880 .expect("Ssl build error.") 881 .into_inner(); 882 } 883 884 /// UT test cases for `TlsConfig::ssl` and `SslRef::set_verify_hostname`. 885 /// 886 /// # Brief 887 /// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and 888 /// `TlsConfigBuilder::build`. 889 /// 2. Sets hostname "" and verify_hostname. 890 /// 3. Creates a `Ssl` by calling `TlsConfig::ssl_new` then creates a 891 /// `SslStream`. 892 /// 4. Calls `write` and `read` by `SslStream`. 893 /// 5. Checks if retures the segmentation fault `invalid memory reference`. 894 #[cfg(feature = "sync")] 895 #[test] ut_tls_ssl_verify_hostname()896 fn ut_tls_ssl_verify_hostname() { 897 use std::io::{Read, Write}; 898 use std::net::TcpStream; 899 900 let config = TlsConfigBuilder::new() 901 .sni(false) 902 .danger_accept_invalid_hostnames(false) 903 .build() 904 .expect("TlsConfig build error."); 905 906 let domain = String::from(""); 907 let ssl = config 908 .ssl_new(domain.as_str()) 909 .expect("Ssl build error.") 910 .into_inner(); 911 let stream = TcpStream::connect("huawei.com:443").expect("Tcp stream error."); 912 let mut tls_stream = ssl.connect(stream).expect("Tls stream error."); 913 914 tls_stream 915 .write_all(b"GET / HTTP/1.0\r\n\r\n") 916 .expect("Stream write error."); 917 let mut res = vec![]; 918 tls_stream 919 .read_to_end(&mut res) 920 .expect("Stream read error."); 921 println!("{}", String::from_utf8_lossy(&res)); 922 } 923 924 /// UT test cases for `Cert::from_pem`. 925 /// 926 /// # Brief 927 /// 1. Creates a `Cert` by calling `Cert::from_pem`. 928 /// 2. Provides an invalid pem as argument. 929 /// 3. Checks if the result is as expected. 930 #[test] ut_x509_from_pem()931 fn ut_x509_from_pem() { 932 let pem = "(pem-content)"; 933 let x509 = Cert::from_pem(pem.as_bytes()); 934 assert!(x509.is_err()); 935 936 let cert = include_bytes!("../../../tests/file/root-ca.pem"); 937 println!("{:?}", std::str::from_utf8(cert).unwrap()); 938 let x509 = Cert::from_pem(cert); 939 assert!(x509.is_ok()); 940 } 941 942 /// UT test cases for `Cert::from_der`. 943 /// 944 /// # Brief 945 /// 1. Creates a `Cert` by calling `Cert::from_der`. 946 /// 2. Provides an invalid der as argument. 947 /// 3. Checks if the result is as expected. 948 #[test] ut_x509_from_der()949 fn ut_x509_from_der() { 950 let der = "(dar-content)"; 951 let x509 = Cert::from_der(der.as_bytes()); 952 assert!(x509.is_err()); 953 } 954 955 /// UT test cases for `Cert::stack_from_pem`. 956 /// 957 /// # Brief 958 /// 1. Creates a `Cert` by calling `Cert::stack_from_pem`. 959 /// 2. Provides pem bytes as argument. 960 /// 3. Checks if the result is as expected. 961 #[test] ut_cert_stack_from_der()962 fn ut_cert_stack_from_der() { 963 let v = include_bytes!("../../../tests/file/root-ca.pem"); 964 let x509 = Cert::stack_from_pem(v); 965 assert!(x509.is_ok()); 966 } 967 968 /// UT test cases for `Certificate::from_pem`. 969 /// 970 /// # Brief 971 /// 1. Creates a `Certificate` by calling `Certificate::from_pem`. 972 /// 2. Provides pem bytes as argument. 973 /// 3. Checks if the result is as expected. 974 #[test] ut_certificate_from_pem()975 fn ut_certificate_from_pem() { 976 let v = include_bytes!("../../../tests/file/root-ca.pem"); 977 let certs = Certificate::from_pem(v); 978 assert!(certs.is_ok()); 979 } 980 } 981