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