bjorn/validator/
mod.rs

1use foreign_types::{ForeignType, ForeignTypeRef};
2use std::convert::TryFrom;
3use http_body_util::BodyExt;
4use base64::prelude::*;
5
6pub mod caa;
7
8#[derive(Debug)]
9pub enum Identifier {
10    Domain(String, bool),
11    IPAddr(std::net::IpAddr),
12    Email(String),
13}
14
15fn map_identifier(identifier: Option<crate::cert_order::Identifier>) -> Result<Identifier, tonic::Status> {
16    if let Some(identifier) = identifier {
17        Ok(match crate::cert_order::IdentifierType::try_from(identifier.id_type) {
18            Ok(crate::cert_order::IdentifierType::DnsIdentifier) => {
19                let is_wild = identifier.identifier.starts_with("*.");
20                if is_wild {
21                    Identifier::Domain(identifier.identifier[2..].to_string(), true)
22                } else {
23                    Identifier::Domain(identifier.identifier, false)
24                }
25            }
26            Ok(crate::cert_order::IdentifierType::IpIdentifier) => {
27                let ip_addr: std::net::IpAddr = match std::str::FromStr::from_str(&identifier.identifier) {
28                    Ok(a) => a,
29                    Err(_) => return Err(tonic::Status::invalid_argument("Invalid IP address")),
30                };
31                Identifier::IPAddr(ip_addr)
32            }
33            Ok(crate::cert_order::IdentifierType::EmailIdentifier) => {
34                Identifier::Email(identifier.identifier)
35            },
36            _ => return Err(tonic::Status::invalid_argument("Invalid identifier type specified")),
37        })
38    } else {
39        Err(tonic::Status::invalid_argument("Identifier must be specified"))
40    }
41}
42
43impl Identifier {
44    fn to_pb(&self) -> crate::cert_order::Identifier {
45        match self {
46            Identifier::Domain(d, _) => crate::cert_order::Identifier {
47                id_type: crate::cert_order::IdentifierType::DnsIdentifier.into(),
48                identifier: d.clone(),
49            },
50            Identifier::IPAddr(ip) => crate::cert_order::Identifier {
51                id_type: crate::cert_order::IdentifierType::IpIdentifier.into(),
52                identifier: ip.to_string(),
53            },
54            Identifier::Email(e) => crate::cert_order::Identifier {
55                id_type: crate::cert_order::IdentifierType::EmailIdentifier.into(),
56                identifier: e.clone(),
57            }
58        }
59    }
60}
61
62#[derive(Debug)]
63pub struct Validator<S: torrosion::storage::Storage> {
64    dns_resolver: trust_dns_resolver::TokioAsyncResolver,
65    reqwest_client: reqwest::Client,
66    tor_client: Option<torrosion::Client<S>>,
67    caa_identities: Vec<String>,
68}
69
70impl<S: torrosion::storage::Storage + Send + Sync + 'static> Validator<S> {
71    pub async fn new(caa_identities: Vec<String>, storage: Option<S>) -> Self {
72        let resolver = trust_dns_resolver::AsyncResolver::tokio_from_system_conf()
73            .expect("Unable to read DNS config");
74        let client = reqwest::Client::builder()
75            .user_agent(concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION")))
76            .gzip(true)
77            .brotli(true)
78            .deflate(true)
79            .redirect(reqwest::redirect::Policy::limited(10))
80            .referer(true)
81            .no_proxy()
82            .build()
83            .expect("Unable to build HTTP client");
84
85        let tor_client = if let Some(storage) = storage {
86            let mut c = torrosion::Client::new(storage);
87            c.run().await;
88
89            Some(c)
90        } else {
91            None
92        };
93
94        Validator {
95            dns_resolver: resolver,
96            reqwest_client: client,
97            tor_client,
98            caa_identities,
99        }
100    }
101}
102
103async fn check_caa<S: torrosion::storage::Storage + Send + Sync + 'static>(
104    validator: &Validator<S>, identifier: &Identifier, validation_method: &str,
105    account_uri: Option<&str>, hs_priv_key: Option<&[u8; 32]>, onion_caa: Option<&crate::cert_order::OnionCaa>,
106) -> crate::cert_order::ValidationResult {
107    let caa_res = match caa::verify_caa_record(
108        validator, identifier, validation_method, account_uri, hs_priv_key, onion_caa
109    ).await {
110        Ok(r) => r,
111        Err(err) => match err {
112            caa::CAAError::ServFail => return crate::cert_order::ValidationResult {
113                valid: false,
114                error: Some(crate::cert_order::ErrorResponse {
115                    errors: vec![crate::cert_order::Error {
116                        error_type: crate::cert_order::ErrorType::DnsError.into(),
117                        title: "CAA error".to_string(),
118                        detail: "SERVFAIL when checking CAA record".to_string(),
119                        status: 400,
120                        identifier: Some(identifier.to_pb()),
121                        instance: None,
122                        sub_problems: vec![],
123                    }]
124                }),
125            },
126            caa::CAAError::UnsupportedCritical(e) => return crate::cert_order::ValidationResult {
127                valid: false,
128                error: Some(crate::cert_order::ErrorResponse {
129                    errors: vec![crate::cert_order::Error {
130                        error_type: crate::cert_order::ErrorType::CaaError.into(),
131                        title: "CAA error".to_string(),
132                        detail: format!("Unsupported critical CAA record: {}", e),
133                        status: 400,
134                        identifier: Some(identifier.to_pb()),
135                        instance: None,
136                        sub_problems: vec![],
137                    }]
138                }),
139            },
140            caa::CAAError::Other(e) => return crate::cert_order::ValidationResult {
141                valid: false,
142                error: Some(crate::cert_order::ErrorResponse {
143                    errors: vec![crate::cert_order::Error {
144                        error_type: crate::cert_order::ErrorType::CaaError.into(),
145                        title: "CAA error".to_string(),
146                        detail: e,
147                        status: 400,
148                        identifier: Some(identifier.to_pb()),
149                        instance: None,
150                        sub_problems: vec![],
151                    }]
152                }),
153            },
154        }
155    };
156
157   if caa_res {
158       crate::cert_order::ValidationResult {
159           valid: true,
160           error: None,
161       }
162   } else {
163        crate::cert_order::ValidationResult {
164            valid: false,
165            error: Some(crate::cert_order::ErrorResponse {
166                errors: vec![crate::cert_order::Error {
167                    error_type: crate::cert_order::ErrorType::CaaError.into(),
168                    title: "CAA error".to_string(),
169                    detail: "CAA policy prohibits issuance".to_string(),
170                    status: 400,
171                    identifier: Some(identifier.to_pb()),
172                    instance: None,
173                    sub_problems: vec![],
174                }]
175            }),
176        }
177    }
178}
179
180trait RW: tokio::io::AsyncRead + tokio::io::AsyncWrite {}
181impl<T> RW for T where T: tokio::io::AsyncRead + tokio::io::AsyncWrite {}
182
183#[tonic::async_trait]
184impl<S: torrosion::storage::Storage + Send + Sync + 'static> crate::cert_order::validator_server::Validator for Validator<S> {
185    async fn check_caa(
186        &self, request: tonic::Request<crate::cert_order::CaaCheckRequest>,
187    ) -> Result<tonic::Response<crate::cert_order::ValidationResult>, tonic::Status> {
188        let req = request.into_inner();
189        let identifier = map_identifier(req.identifier.clone())?;
190
191        let hs_priv_key = if req.hs_private_key.len() == 0 {
192            None
193        } else if req.hs_private_key.len() == 32 {
194            Some(std::convert::TryInto::<[u8; 32]>::try_into(req.hs_private_key.as_slice()).unwrap())
195        } else {
196            return Err(tonic::Status::invalid_argument("hs_priv_key must be 32 bytes long"));
197        };
198
199        let validation_method = match crate::cert_order::ValidationMethod::try_from(req.validation_method) {
200            Ok(crate::cert_order::ValidationMethod::Http01) => "http-01",
201            Ok(crate::cert_order::ValidationMethod::Dns01) => "dns-01",
202            Ok(crate::cert_order::ValidationMethod::TlsAlpn01) => "tls-alpn-01",
203            Ok(crate::cert_order::ValidationMethod::OnionCsr01) => "onion-csr-01",
204            _ => return Err(tonic::Status::invalid_argument("Invalid validation method specified")),
205        };
206
207        Ok(tonic::Response::new( check_caa(
208            self, &identifier, validation_method,
209            req.account_uri.as_deref(), hs_priv_key.as_ref(),
210            req.onion_caa.as_ref()
211        ).await))
212    }
213
214    async fn validate_http01(
215        &self, request: tonic::Request<crate::cert_order::KeyValidationRequest>,
216    ) -> Result<tonic::Response<crate::cert_order::ValidationResult>, tonic::Status> {
217        let req = request.into_inner();
218        let identifier = map_identifier(req.identifier.clone())?;
219
220        let hs_priv_key = if req.hs_private_key.len() == 0 {
221            None
222        } else if req.hs_private_key.len() == 32 {
223            Some(std::convert::TryInto::<[u8; 32]>::try_into(req.hs_private_key.as_slice()).unwrap())
224        } else {
225            return Err(tonic::Status::invalid_argument("hs_priv_key must be 32 bytes long"));
226        };
227
228        let (test_uri, is_tor) = match identifier {
229            Identifier::Domain(domain, wildcard) => {
230                if wildcard {
231                    return Err(tonic::Status::invalid_argument("http-01 must not be used for wildcard domains"));
232                }
233
234                (format!("http://{}:80/.well-known/acme-challenge/{}", domain, req.token), domain.ends_with(".onion"))
235            },
236            Identifier::IPAddr(ip) => (match ip {
237                std::net::IpAddr::V4(ipv4) => format!("http://{}:80/.well-known/acme-challenge/{}", ipv4, req.token),
238                std::net::IpAddr::V6(ipv6) => format!("http://[{}]:80/.well-known/acme-challenge/{}", ipv6, req.token),
239            }, false),
240            Identifier::Email(_) => return Err(tonic::Status::invalid_argument("http-01 makes no sense for email"))
241        };
242        let key_auth = format!("{}.{}", req.token, req.account_thumbprint);
243
244        let uri_error = crate::cert_order::ErrorResponse {
245            errors: vec![crate::cert_order::Error {
246                error_type: crate::cert_order::ErrorType::ConnectionError.into(),
247                title: "Validation failed".to_string(),
248                detail: "Invalid URI".to_string(),
249                status: 400,
250                identifier: req.identifier.clone(),
251                instance: None,
252                sub_problems: vec![],
253            }]
254        };
255        let timeout_error = crate::cert_order::ErrorResponse {
256            errors: vec![crate::cert_order::Error {
257                error_type: crate::cert_order::ErrorType::ConnectionError.into(),
258                title: "Validation failed".to_string(),
259                detail: "Connection timed out".to_string(),
260                status: 400,
261                identifier: req.identifier.clone(),
262                instance: None,
263                sub_problems: vec![],
264            }]
265        };
266        let connect_error = crate::cert_order::ErrorResponse {
267            errors: vec![crate::cert_order::Error {
268                error_type: crate::cert_order::ErrorType::ConnectionError.into(),
269                title: "Validation failed".to_string(),
270                detail: "Connection refused".to_string(),
271                status: 400,
272                identifier: req.identifier.clone(),
273                instance: None,
274                sub_problems: vec![],
275            }]
276        };
277        let other_error = crate::cert_order::ErrorResponse {
278            errors: vec![crate::cert_order::Error {
279                error_type: crate::cert_order::ErrorType::ConnectionError.into(),
280                title: "Validation failed".to_string(),
281                detail: "Unknown request error".to_string(),
282                status: 400,
283                identifier: req.identifier.clone(),
284                instance: None,
285                sub_problems: vec![],
286            }]
287        };
288        let charset_error = crate::cert_order::ErrorResponse {
289            errors: vec![crate::cert_order::Error {
290                error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
291                title: "Validation failed".to_string(),
292                detail: "Text charset error".to_string(),
293                status: 400,
294                identifier: req.identifier.clone(),
295                instance: None,
296                sub_problems: vec![],
297            }]
298        };
299
300        let (status, resp_txt) = if is_tor {
301            let client = match self.tor_client {
302                Some(ref c) => c.clone(),
303                None => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
304                    valid: false,
305                    error: Some(crate::cert_order::ErrorResponse {
306                        errors: vec![crate::cert_order::Error {
307                            error_type: crate::cert_order::ErrorType::UnsupportedIdentifierError.into(),
308                            title: "Validation failed".to_string(),
309                            detail: "Hidden services are not supported".to_string(),
310                            status: 400,
311                            identifier: req.identifier,
312                            instance: None,
313                            sub_problems: vec![],
314                        }]
315                    }),
316                }))
317            };
318
319            let test_uri = match hyper::Uri::try_from(&test_uri) {
320                Ok(u) => u,
321                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
322                    valid: false,
323                    error: Some(uri_error),
324                }))
325            };
326
327            let hs_client = torrosion::hs::http::new_hs_client(client, hs_priv_key);
328
329            let resp = match hs_client.get(test_uri).await {
330                Ok(u) => u,
331                Err(err) => {
332                    debug!("Error connecting to hidden service validation endpoint: {}", err);
333                    return if err.is_connect() {
334                        Ok(tonic::Response::new(crate::cert_order::ValidationResult {
335                            valid: false,
336                            error: Some(connect_error),
337                        }))
338                    } else {
339                        Ok(tonic::Response::new(crate::cert_order::ValidationResult {
340                            valid: false,
341                            error: Some(other_error),
342                        }))
343                    }
344                }
345            };
346
347            (resp.status(), match resp.collect().await {
348                Ok(b) => match String::from_utf8(b.to_bytes().to_vec()) {
349                    Ok(s) => s,
350                    Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
351                        valid: false,
352                        error: Some(charset_error),
353                    }))
354                },
355                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
356                    valid: false,
357                    error: Some(other_error),
358                }))
359            })
360        } else {
361            let test_uri = match reqwest::Url::parse(&test_uri) {
362                Ok(u) => u,
363                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
364                    valid: false,
365                    error: Some(uri_error),
366                }))
367            };
368            let resp = match self.reqwest_client.get(test_uri).send().await {
369                Ok(u) => u,
370                Err(err) => {
371                    debug!("Error connecting to validation endpoint: {}", err);
372                    return if err.is_timeout() {
373                        Ok(tonic::Response::new(crate::cert_order::ValidationResult {
374                            valid: false,
375                            error: Some(timeout_error),
376                        }))
377                    } else if err.is_connect() {
378                        Ok(tonic::Response::new(crate::cert_order::ValidationResult {
379                            valid: false,
380                            error: Some(connect_error),
381                        }))
382                    } else if err.is_redirect() {
383                        Ok(tonic::Response::new(crate::cert_order::ValidationResult {
384                            valid: false,
385                            error: Some(crate::cert_order::ErrorResponse {
386                                errors: vec![crate::cert_order::Error {
387                                    error_type: crate::cert_order::ErrorType::ConnectionError.into(),
388                                    title: "Validation failed".to_string(),
389                                    detail: "Too many redirects".to_string(),
390                                    status: 400,
391                                    identifier: req.identifier,
392                                    instance: None,
393                                    sub_problems: vec![],
394                                }]
395                            }),
396                        }))
397                    } else {
398                        Ok(tonic::Response::new(crate::cert_order::ValidationResult {
399                            valid: false,
400                            error: Some(other_error),
401                        }))
402                    }
403                }
404            };
405
406            (resp.status(), match resp.text().await {
407                Ok(t) => t,
408                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
409                    valid: false,
410                    error: Some(charset_error),
411                }))
412            })
413        };
414
415        if !status.is_success() {
416            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
417                valid: false,
418                error: Some(crate::cert_order::ErrorResponse {
419                    errors: vec![crate::cert_order::Error {
420                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
421                        title: "Validation failed".to_string(),
422                        detail: format!("HTTP {} received", status.as_str()),
423                        status: 400,
424                        identifier: req.identifier,
425                        instance: None,
426                        sub_problems: vec![],
427                    }]
428                }),
429            }));
430        }
431
432        if resp_txt.trim() == key_auth {
433            Ok(tonic::Response::new(crate::cert_order::ValidationResult {
434                valid: true,
435                error: None,
436            }))
437        } else {
438            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
439                valid: false,
440                error: Some(crate::cert_order::ErrorResponse {
441                    errors: vec![crate::cert_order::Error {
442                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
443                        title: "Validation failed".to_string(),
444                        detail: format!("Expected '{}', received '{}'", key_auth, resp_txt),
445                        status: 400,
446                        identifier: req.identifier,
447                        instance: None,
448                        sub_problems: vec![],
449                    }]
450                }),
451            }));
452        }
453    }
454
455    async fn validate_dns01(
456        &self, request: tonic::Request<crate::cert_order::KeyValidationRequest>,
457    ) -> Result<tonic::Response<crate::cert_order::ValidationResult>, tonic::Status> {
458        let req = request.into_inner();
459        let identifier = map_identifier(req.identifier.clone())?;
460
461        let key_auth = format!("{}.{}", req.token, req.account_thumbprint);
462        let key_auth_hash_bytes = openssl::hash::hash(openssl::hash::MessageDigest::sha256(), key_auth.as_bytes()).unwrap().to_vec();
463        let key_auth_hash = BASE64_URL_SAFE_NO_PAD.encode(&key_auth_hash_bytes);
464        let key_auth_hash_utf8 = key_auth_hash.as_bytes();
465
466        let search_domain = match identifier {
467            Identifier::Domain(domain, _) => {
468                if domain.ends_with(".onion") {
469                    return Err(tonic::Status::invalid_argument("dns-01 must not be used for .onion domains"))
470                }
471                format!("_acme-challenge.{}.", domain.trim_end_matches('.'))
472            },
473            Identifier::IPAddr(_) => return Err(tonic::Status::invalid_argument("dns-01 must not be used for IP addresses")),
474            Identifier::Email(_) => return Err(tonic::Status::invalid_argument("dns-01 makes no sense for email")),
475        };
476        match self.dns_resolver.txt_lookup(search_domain.clone()).await {
477            Ok(r) => {
478                for record in r.iter(){
479                    if let Some(data) = record.txt_data().iter().next().as_deref() {
480                        if data.as_ref() == key_auth_hash_utf8 {
481                            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
482                                valid: true,
483                                error: None,
484                            }));
485                        }
486                    }
487                }
488
489                Ok(tonic::Response::new(crate::cert_order::ValidationResult {
490                    valid: false,
491                    error: Some(crate::cert_order::ErrorResponse {
492                        errors: vec![crate::cert_order::Error {
493                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
494                            title: "Validation failed".to_string(),
495                            detail: format!("No TXT records found for {} with the value '{}'", search_domain, key_auth_hash),
496                            status: 400,
497                            identifier: req.identifier,
498                            instance: None,
499                            sub_problems: vec![],
500                        }]
501                    }),
502                }))
503            }
504            Err(err) => match err.kind() {
505                trust_dns_resolver::error::ResolveErrorKind::NoRecordsFound { .. } => Ok(tonic::Response::new(crate::cert_order::ValidationResult {
506                    valid: false,
507                    error: Some(crate::cert_order::ErrorResponse {
508                        errors: vec![crate::cert_order::Error {
509                            error_type: crate::cert_order::ErrorType::DnsError.into(),
510                            title: "Validation failed".to_string(),
511                            detail: format!("No TXT records found for {}", search_domain),
512                            status: 400,
513                            identifier: req.identifier,
514                            instance: None,
515                            sub_problems: vec![],
516                        }]
517                    }),
518                })),
519                _ => Ok(tonic::Response::new(crate::cert_order::ValidationResult {
520                    valid: false,
521                    error: Some(crate::cert_order::ErrorResponse {
522                        errors: vec![crate::cert_order::Error {
523                            error_type: crate::cert_order::ErrorType::DnsError.into(),
524                            title: "Validation failed".to_string(),
525                            detail: format!("SERVFAIL whilst getting records for {}", search_domain),
526                            status: 400,
527                            identifier: req.identifier,
528                            instance: None,
529                            sub_problems: vec![],
530                        }]
531                    }),
532                }))
533            }
534        }
535    }
536
537    async fn validate_tlsalpn01(
538        &self, request: tonic::Request<crate::cert_order::KeyValidationRequest>,
539    ) -> Result<tonic::Response<crate::cert_order::ValidationResult>, tonic::Status> {
540        let req = request.into_inner();
541        let identifier = map_identifier(req.identifier.clone())?;
542
543        let hs_priv_key = if req.hs_private_key.len() == 0 {
544            None
545        } else if req.hs_private_key.len() == 32 {
546            Some(std::convert::TryInto::<[u8; 32]>::try_into(req.hs_private_key.as_slice()).unwrap())
547        } else {
548            return Err(tonic::Status::invalid_argument("hs_priv_key must be 32 bytes long"));
549        };
550
551        let (connection_string, sni_string, ip_bytes, is_tor) = match identifier {
552            Identifier::Domain(domain, wildcard) => {
553                if wildcard {
554                    return Err(tonic::Status::invalid_argument("tls-alpn-01 must not be used for wildcard domains"));
555                }
556
557                (format!("{}:443", domain), domain.to_ascii_lowercase(), vec![], domain.ends_with(".onion"))
558            },
559            Identifier::IPAddr(ip) => match ip {
560                std::net::IpAddr::V4(ipv4) =>
561                    (format!("{}:443", ipv4), trust_dns_resolver::Name::from(ipv4).to_ascii(), ipv4.octets().to_vec(), false),
562                std::net::IpAddr::V6(ipv6) =>
563                    (format!("[{}]:443", ipv6), trust_dns_resolver::Name::from(ipv6).to_ascii(), ipv6.octets().to_vec(), false),
564            },
565            Identifier::Email(_) => return Err(tonic::Status::invalid_argument("tls-alpn-01 makes no sense for email"))
566        };
567        let key_auth = format!("{}.{}", req.token, req.account_thumbprint);
568        let key_auth_hash_bytes = openssl::hash::hash(openssl::hash::MessageDigest::sha256(), key_auth.as_bytes()).unwrap().to_vec();
569
570        let mut ssl_ctx_builder = match openssl::ssl::SslContext::builder(openssl::ssl::SslMethod::tls_client()) {
571            Ok(b) => b,
572            Err(err) => {
573                error!("Failed to create SSL context builder: {}", err);
574                return Err(tonic::Status::internal("failed to create SSL context"));
575            }
576        };
577        ssl_ctx_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
578        ssl_ctx_builder.set_min_proto_version(Some(openssl::ssl::SslVersion::TLS1_2)).unwrap();
579        ssl_ctx_builder.set_alpn_protos(b"\x0aacme-tls/1").unwrap();
580        let ssl_ctx = ssl_ctx_builder.build();
581
582        let tcp_stream: std::pin::Pin<Box<dyn RW + Send>> = if is_tor {
583            let client = match self.tor_client {
584                Some(ref c) => c,
585                None => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
586                    valid: false,
587                    error: Some(crate::cert_order::ErrorResponse {
588                        errors: vec![crate::cert_order::Error {
589                            error_type: crate::cert_order::ErrorType::UnsupportedIdentifierError.into(),
590                            title: "Validation failed".to_string(),
591                            detail: "Hidden services are not supported".to_string(),
592                            status: 400,
593                            identifier: req.identifier,
594                            instance: None,
595                            sub_problems: vec![],
596                        }]
597                    }),
598                }))
599            };
600
601            let hs_address = match torrosion::hs::HSAddress::from_str(&sni_string) {
602                Ok(hs) => hs,
603                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
604                    valid: false,
605                    error: Some(crate::cert_order::ErrorResponse {
606                        errors: vec![crate::cert_order::Error {
607                            error_type: crate::cert_order::ErrorType::ConnectionError.into(),
608                            title: "Connection failed".to_string(),
609                            detail: "Malformed HS address".to_string(),
610                            status: 400,
611                            identifier: req.identifier,
612                            instance: None,
613                            sub_problems: vec![],
614                        }]
615                    }),
616                }))
617            };
618
619            let (ds, subcred) = match hs_address.fetch_ds(&client, hs_priv_key).await {
620                Ok(v) => v,
621                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
622                    valid: false,
623                    error: Some(crate::cert_order::ErrorResponse {
624                        errors: vec![crate::cert_order::Error {
625                            error_type: crate::cert_order::ErrorType::ConnectionError.into(),
626                            title: "Connection failed".to_string(),
627                            detail: "Failed to get HS descriptor".to_string(),
628                            status: 400,
629                            identifier: req.identifier,
630                            instance: None,
631                            sub_problems: vec![],
632                        }]
633                    }),
634                }))
635            };
636            let hs_circ = match torrosion::hs::con::connect(&client, &ds, &subcred).await {
637                Ok(v) => v,
638                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
639                    valid: false,
640                    error: Some(crate::cert_order::ErrorResponse {
641                        errors: vec![crate::cert_order::Error {
642                            error_type: crate::cert_order::ErrorType::ConnectionError.into(),
643                            title: "Connection failed".to_string(),
644                            detail: "Failed to connect to HS".to_string(),
645                            status: 400,
646                            identifier: req.identifier,
647                            instance: None,
648                            sub_problems: vec![],
649                        }]
650                    }),
651                }))
652            };
653
654            Box::pin(hs_circ.relay_begin(&connection_string, None).await?)
655        } else {
656            match tokio::net::TcpStream::connect(connection_string.clone()).await {
657                Ok(s) => Box::pin(s),
658                Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
659                    valid: false,
660                    error: Some(crate::cert_order::ErrorResponse {
661                        errors: vec![crate::cert_order::Error {
662                            error_type: crate::cert_order::ErrorType::ConnectionError.into(),
663                            title: "Connection failed".to_string(),
664                            detail: format!("Failed to open TCP connection to {}", connection_string),
665                            status: 400,
666                            identifier: req.identifier,
667                            instance: None,
668                            sub_problems: vec![],
669                        }]
670                    }),
671                }))
672            }
673        };
674
675        let mut ssl_session = match openssl::ssl::Ssl::new(&ssl_ctx) {
676            Ok(b) => b,
677            Err(err) => {
678                error!("Failed to create SSL session: {}", err);
679                return Err(tonic::Status::internal("failed to create SSL session"));
680            }
681        };
682        ssl_session.set_hostname(&sni_string).unwrap();
683        let mut ssl_stream = match tokio_openssl::SslStream::new(ssl_session, tcp_stream) {
684            Ok(b) => b,
685            Err(err) => {
686                error!("Failed to create SSL stream: {}", err);
687                return Err(tonic::Status::internal("failed to create SSL stream"));
688            }
689        };
690
691        match std::pin::Pin::new(&mut ssl_stream).connect().await {
692            Ok(_) => {}
693            Err(_) => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
694                valid: false,
695                error: Some(crate::cert_order::ErrorResponse {
696                    errors: vec![crate::cert_order::Error {
697                        error_type: crate::cert_order::ErrorType::TlsError.into(),
698                        title: "Connection failed".to_string(),
699                        detail: format!("Failed to negotiate TLS connection with {}", connection_string),
700                        status: 400,
701                        identifier: req.identifier,
702                        instance: None,
703                        sub_problems: vec![],
704                    }]
705                }),
706            }))
707        }
708
709        let ssl_session_ref = ssl_stream.ssl();
710        let acme_identifier_oid = openssl::asn1::Asn1Object::from_str("1.3.6.1.5.5.7.1.31").unwrap();
711        let selected_alpn_protocol = ssl_session_ref.selected_alpn_protocol();
712        let peer_certificate = match ssl_session_ref.peer_certificate() {
713            Some(c) => c,
714            None => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
715                valid: false,
716                error: Some(crate::cert_order::ErrorResponse {
717                    errors: vec![crate::cert_order::Error {
718                        error_type: crate::cert_order::ErrorType::TlsError.into(),
719                        title: "No certificate".to_string(),
720                        detail: "Server did not return a self signed certificate".to_string(),
721                        status: 400,
722                        identifier: req.identifier,
723                        instance: None,
724                        sub_problems: vec![],
725                    }]
726                }),
727            }))
728        };
729
730        if selected_alpn_protocol != Some(b"acme-tls/1") {
731            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
732                valid: false,
733                error: Some(crate::cert_order::ErrorResponse {
734                    errors: vec![crate::cert_order::Error {
735                        error_type: crate::cert_order::ErrorType::TlsError.into(),
736                        title: "ALPN failed".to_string(),
737                        detail: "Server did not negotiate \"acme-tls/1\" protocol".to_string(),
738                        status: 400,
739                        identifier: req.identifier,
740                        instance: None,
741                        sub_problems: vec![],
742                    }]
743                }),
744            }));
745        }
746
747        let mut subject_alt_names = match peer_certificate.subject_alt_names() {
748            Some(n) => n,
749            None => return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
750                valid: false,
751                error: Some(crate::cert_order::ErrorResponse {
752                    errors: vec![crate::cert_order::Error {
753                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
754                        title: "No SANs".to_string(),
755                        detail: "Server did not return a SAN in its self signed certificate".to_string(),
756                        status: 400,
757                        identifier: req.identifier,
758                        instance: None,
759                        sub_problems: vec![],
760                    }]
761                }),
762            }))
763        };
764
765        if subject_alt_names.len() != 1 {
766            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
767                valid: false,
768                error: Some(crate::cert_order::ErrorResponse {
769                    errors: vec![crate::cert_order::Error {
770                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
771                        title: "Invalid SANs".to_string(),
772                        detail: "Server did not return only one SAN in its self signed certificate".to_string(),
773                        status: 400,
774                        identifier: req.identifier,
775                        instance: None,
776                        sub_problems: vec![],
777                    }]
778                }),
779            }));
780        }
781        let subject_alt_name = subject_alt_names.pop().unwrap();
782        if ip_bytes.is_empty() {
783            if let Some(domain_alt_name) = subject_alt_name.dnsname() {
784                if domain_alt_name.to_ascii_lowercase() != sni_string {
785                    return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
786                        valid: false,
787                        error: Some(crate::cert_order::ErrorResponse {
788                            errors: vec![crate::cert_order::Error {
789                                error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
790                                title: "Invalid SANs".to_string(),
791                                detail: format!("Server returned a SAN for '{}' its self signed certificate, expected '{}'", domain_alt_name, sni_string),
792                                status: 400,
793                                identifier: req.identifier,
794                                instance: None,
795                                sub_problems: vec![],
796                            }]
797                        }),
798                    }));
799                }
800            } else {
801                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
802                    valid: false,
803                    error: Some(crate::cert_order::ErrorResponse {
804                        errors: vec![crate::cert_order::Error {
805                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
806                            title: "Invalid SANs".to_string(),
807                            detail: "Server did not return a domain SAN in its self signed certificate".to_string(),
808                            status: 400,
809                            identifier: req.identifier,
810                            instance: None,
811                            sub_problems: vec![],
812                        }]
813                    }),
814                }));
815            }
816        } else {
817            if let Some(ip_alt_name) = subject_alt_name.ipaddress() {
818                if ip_alt_name != ip_bytes {
819                    return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
820                        valid: false,
821                        error: Some(crate::cert_order::ErrorResponse {
822                            errors: vec![crate::cert_order::Error {
823                                error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
824                                title: "Invalid SANs".to_string(),
825                                detail: format!("Server returned a SAN for '{:X?}' its self signed certificate, expected '{:X?}'", ip_alt_name, ip_bytes),
826                                status: 400,
827                                identifier: req.identifier,
828                                instance: None,
829                                sub_problems: vec![],
830                            }]
831                        }),
832                    }));
833                }
834            } else {
835                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
836                    valid: false,
837                    error: Some(crate::cert_order::ErrorResponse {
838                        errors: vec![crate::cert_order::Error {
839                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
840                            title: "Invalid SANs".to_string(),
841                            detail: "Server did not return an IP SAN in its self signed certificate".to_string(),
842                            status: 400,
843                            identifier: req.identifier,
844                            instance: None,
845                            sub_problems: vec![],
846                        }]
847                    }),
848                }));
849            }
850        }
851
852        let acme_id_data_bytes = unsafe {
853            let extensions = openssl_sys::X509_get0_extensions(peer_certificate.as_ptr());
854            let acme_id_idx = X509v3_get_ext_by_OBJ(extensions, acme_identifier_oid.as_ptr(), -1);
855            if acme_id_idx < 0 {
856                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
857                    valid: false,
858                    error: Some(crate::cert_order::ErrorResponse {
859                        errors: vec![crate::cert_order::Error {
860                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
861                            title: "No acmeIdentifier extensions".to_string(),
862                            detail: "Server did not return a acmeIdentifier with the key authorization in its self signed certificate".to_string(),
863                            status: 400,
864                            identifier: req.identifier,
865                            instance: None,
866                            sub_problems: vec![],
867                        }]
868                    }),
869                }));
870            }
871
872            let acme_id_ext = openssl_sys::X509v3_get_ext(extensions, acme_id_idx);
873
874            if openssl_sys::X509_EXTENSION_get_critical(acme_id_ext) != 1 {
875                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
876                    valid: false,
877                    error: Some(crate::cert_order::ErrorResponse {
878                        errors: vec![crate::cert_order::Error {
879                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
880                            title: "Invalid acmeIdentifier extension".to_string(),
881                            detail: "Server returned a non critical acmeIdentifier extension in its self signed certificate".to_string(),
882                            status: 400,
883                            identifier: req.identifier,
884                            instance: None,
885                            sub_problems: vec![],
886                        }]
887                    }),
888                }));
889            }
890
891            let acme_id_ext_data = openssl_sys::X509_EXTENSION_get_data(acme_id_ext);
892            let acme_id_data = match crate::util::cvt_p(d2i_ASN1_OCTET_STRING(
893                std::ptr::null_mut(),
894                &mut openssl_sys::ASN1_STRING_get0_data(acme_id_ext_data as *const openssl_sys::ASN1_STRING),
895                openssl_sys::ASN1_STRING_length(acme_id_ext_data as *const openssl_sys::ASN1_STRING) as libc::c_long,
896            )) {
897                Ok(d) => d,
898                Err(_) => {
899                    return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
900                        valid: false,
901                        error: Some(crate::cert_order::ErrorResponse {
902                            errors: vec![crate::cert_order::Error {
903                                error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
904                                title: "Invalid acmeIdentifier extension".to_string(),
905                                detail: "Server returned an un-parsable acmeIdentifier extension in its self signed certificate".to_string(),
906                                status: 400,
907                                identifier: req.identifier,
908                                instance: None,
909                                sub_problems: vec![],
910                            }]
911                        }),
912                    }));
913                }
914            };
915            std::slice::from_raw_parts(
916                openssl_sys::ASN1_STRING_get0_data(acme_id_data as *const openssl_sys::ASN1_STRING),
917                openssl_sys::ASN1_STRING_length(acme_id_data as *const openssl_sys::ASN1_STRING) as usize,
918            )
919        };
920
921        Ok(tonic::Response::new(if acme_id_data_bytes == key_auth_hash_bytes {
922            crate::cert_order::ValidationResult {
923                valid: true,
924                error: None,
925            }
926        } else {
927            crate::cert_order::ValidationResult {
928                valid: false,
929                error: Some(crate::cert_order::ErrorResponse {
930                    errors: vec![crate::cert_order::Error {
931                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
932                        title: "Invalid acmeIdentifier extension".to_string(),
933                        detail: format!("Server returned '{:X?}', expected '{:X?}'", acme_id_data_bytes, key_auth_hash_bytes),
934                        status: 400,
935                        identifier: req.identifier,
936                        instance: None,
937                        sub_problems: vec![],
938                    }]
939                }),
940            }
941        }))
942    }
943
944    async fn validate_onion_csr01(
945        &self, request: tonic::Request<crate::cert_order::OnionCsrValidationRequest>,
946    ) -> Result<tonic::Response<crate::cert_order::ValidationResult>, tonic::Status> {
947        let req = request.into_inner();
948        let identifier = map_identifier(req.identifier.clone())?;
949
950        let hs_addr = match identifier {
951            Identifier::Domain(domain, _) => {
952                if !domain.ends_with(".onion") {
953                    return Err(tonic::Status::invalid_argument("onion-csr-01 must not be used for non .onion domains"))
954                }
955                match torrosion::hs::HSAddress::from_str(&domain) {
956                    Ok(addr) => addr,
957                    Err(_) => {
958                        return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
959                            valid: false,
960                            error: Some(crate::cert_order::ErrorResponse {
961                                errors: vec![crate::cert_order::Error {
962                                    error_type: crate::cert_order::ErrorType::UnsupportedIdentifierError.into(),
963                                    title: "Invalid domain".to_string(),
964                                    detail: "Malformed HS address".to_string(),
965                                    status: 400,
966                                    identifier: req.identifier,
967                                    instance: None,
968                                    sub_problems: vec![],
969                                }]
970                            }),
971                        }))
972                    }
973                }
974            },
975            Identifier::IPAddr(_) => return Err(tonic::Status::invalid_argument("onion-csr-01 must not be used for IP addresses")),
976            Identifier::Email(_) => return Err(tonic::Status::invalid_argument("onion-csr-01 makes no sense for email")),
977        };
978
979        let addr_pub_key = openssl::pkey::PKey::public_key_from_raw_bytes(
980            &hs_addr.key, openssl::pkey::Id::ED25519
981        ).unwrap();
982
983        let csr = match openssl::x509::X509Req::from_der(&req.csr) {
984            Ok(csr) => csr,
985            Err(_) => {
986                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
987                    valid: false,
988                    error: Some(crate::cert_order::ErrorResponse {
989                        errors: vec![crate::cert_order::Error {
990                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
991                            title: "Invalid CSR".to_string(),
992                            detail: "CSR could not be parsed".to_string(),
993                            status: 400,
994                            identifier: req.identifier,
995                            instance: None,
996                            sub_problems: vec![],
997                        }]
998                    }),
999                }));
1000            }
1001        };
1002
1003        let req_pub_key = match csr.public_key() {
1004            Ok(k) => k,
1005            Err(_) => {
1006                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1007                    valid: false,
1008                    error: Some(crate::cert_order::ErrorResponse {
1009                        errors: vec![crate::cert_order::Error {
1010                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1011                            title: "Invalid CSR".to_string(),
1012                            detail: "CSR could not be parsed".to_string(),
1013                            status: 400,
1014                            identifier: req.identifier,
1015                            instance: None,
1016                            sub_problems: vec![],
1017                        }]
1018                    }),
1019                }));
1020            }
1021        };
1022
1023        if !req_pub_key.public_eq(&addr_pub_key) {
1024            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1025                valid: false,
1026                error: Some(crate::cert_order::ErrorResponse {
1027                    errors: vec![crate::cert_order::Error {
1028                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1029                        title: "Invalid CSR".to_string(),
1030                        detail: "CSR public key does not match public key of .onion domain".to_string(),
1031                        status: 400,
1032                        identifier: req.identifier,
1033                        instance: None,
1034                        sub_problems: vec![],
1035                    }]
1036                }),
1037            }));
1038        }
1039
1040        if !csr.verify(&addr_pub_key).map_or(false, |r| r) {
1041            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1042                valid: false,
1043                error: Some(crate::cert_order::ErrorResponse {
1044                    errors: vec![crate::cert_order::Error {
1045                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1046                        title: "Invalid CSR".to_string(),
1047                        detail: "CSR signature invalid".to_string(),
1048                        status: 400,
1049                        identifier: req.identifier,
1050                        instance: None,
1051                        sub_problems: vec![],
1052                    }]
1053                }),
1054            }));
1055        }
1056
1057        let ca_nonce_oid = openssl::asn1::Asn1Object::from_str("2.23.140.41").unwrap();
1058        let applicant_nonce_oid = openssl::asn1::Asn1Object::from_str("2.23.140.42").unwrap();
1059
1060        let ca_nonce_at_i = unsafe {
1061            X509_REQ_get_attr_by_OBJ(csr.as_ptr(), ca_nonce_oid.as_ptr(), -1)
1062        };
1063        let applicant_nonce_at_i = unsafe {
1064            X509_REQ_get_attr_by_OBJ(csr.as_ptr(), applicant_nonce_oid.as_ptr(), -1)
1065        };
1066
1067        if ca_nonce_at_i < 0 {
1068            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1069                valid: false,
1070                error: Some(crate::cert_order::ErrorResponse {
1071                    errors: vec![crate::cert_order::Error {
1072                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1073                        title: "Invalid CSR".to_string(),
1074                        detail: "CA nonce attribute not present".to_string(),
1075                        status: 400,
1076                        identifier: req.identifier,
1077                        instance: None,
1078                        sub_problems: vec![],
1079                    }]
1080                }),
1081            }));
1082        }
1083
1084        if applicant_nonce_at_i < 0 {
1085            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1086                valid: false,
1087                error: Some(crate::cert_order::ErrorResponse {
1088                    errors: vec![crate::cert_order::Error {
1089                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1090                        title: "Invalid CSR".to_string(),
1091                        detail: "Applicanct nonce attribute not present".to_string(),
1092                        status: 400,
1093                        identifier: req.identifier,
1094                        instance: None,
1095                        sub_problems: vec![],
1096                    }]
1097                }),
1098            }));
1099        }
1100
1101        let ca_nonce_at_i2 = unsafe {
1102            X509_REQ_get_attr_by_OBJ(csr.as_ptr(), ca_nonce_oid.as_ptr(), ca_nonce_at_i)
1103        };
1104        let applicant_nonce_at_i2 = unsafe {
1105            X509_REQ_get_attr_by_OBJ(csr.as_ptr(), applicant_nonce_oid.as_ptr(), applicant_nonce_at_i)
1106        };
1107
1108        if ca_nonce_at_i2 >= 0 {
1109            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1110                valid: false,
1111                error: Some(crate::cert_order::ErrorResponse {
1112                    errors: vec![crate::cert_order::Error {
1113                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1114                        title: "Invalid CSR".to_string(),
1115                        detail: "Multiple CA nonce attributes present".to_string(),
1116                        status: 400,
1117                        identifier: req.identifier,
1118                        instance: None,
1119                        sub_problems: vec![],
1120                    }]
1121                }),
1122            }));
1123        }
1124
1125        if applicant_nonce_at_i2 >= 0 {
1126            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1127                valid: false,
1128                error: Some(crate::cert_order::ErrorResponse {
1129                    errors: vec![crate::cert_order::Error {
1130                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1131                        title: "Invalid CSR".to_string(),
1132                        detail: "Multiple applicant nonce attributes present".to_string(),
1133                        status: 400,
1134                        identifier: req.identifier,
1135                        instance: None,
1136                        sub_problems: vec![],
1137                    }]
1138                }),
1139            }));
1140        }
1141
1142        let ca_nonce_attr = unsafe {
1143            X509_REQ_get_attr(csr.as_ptr(), ca_nonce_at_i)
1144        };
1145        let applicant_nonce_attr = unsafe {
1146            X509_REQ_get_attr(csr.as_ptr(), applicant_nonce_at_i)
1147        };
1148
1149        let ca_nonce_attr_count = unsafe {
1150            X509_ATTRIBUTE_count(ca_nonce_attr)
1151        };
1152        let applicant_nonce_attr_count = unsafe {
1153            X509_ATTRIBUTE_count(applicant_nonce_attr)
1154        };
1155
1156        if ca_nonce_attr_count != 1 {
1157            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1158                valid: false,
1159                error: Some(crate::cert_order::ErrorResponse {
1160                    errors: vec![crate::cert_order::Error {
1161                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1162                        title: "Invalid CSR".to_string(),
1163                        detail: "CA nonce attribute doesn't contain only one value".to_string(),
1164                        status: 400,
1165                        identifier: req.identifier,
1166                        instance: None,
1167                        sub_problems: vec![],
1168                    }]
1169                }),
1170            }));
1171        }
1172
1173        if applicant_nonce_attr_count != 1 {
1174            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1175                valid: false,
1176                error: Some(crate::cert_order::ErrorResponse {
1177                    errors: vec![crate::cert_order::Error {
1178                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1179                        title: "Invalid CSR".to_string(),
1180                        detail: "Applicant nonce attribute doesn't contain only one value".to_string(),
1181                        status: 400,
1182                        identifier: req.identifier,
1183                        instance: None,
1184                        sub_problems: vec![],
1185                    }]
1186                }),
1187            }));
1188        }
1189
1190        let ca_nonce_attr_value = match unsafe {
1191            crate::util::cvt_p(X509_ATTRIBUTE_get0_data(
1192                ca_nonce_attr, 0, openssl_sys::V_ASN1_OCTET_STRING, std::ptr::null()
1193            ))
1194        } {
1195            Ok(v) => unsafe {
1196                openssl::asn1::Asn1StringRef::from_ptr(v as *mut openssl_sys::ASN1_STRING)
1197            },
1198            Err(_) => {
1199                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1200                    valid: false,
1201                    error: Some(crate::cert_order::ErrorResponse {
1202                        errors: vec![crate::cert_order::Error {
1203                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1204                            title: "Invalid CSR".to_string(),
1205                            detail: "Invalid data type for CA nonce attribute".to_string(),
1206                            status: 400,
1207                            identifier: req.identifier,
1208                            instance: None,
1209                            sub_problems: vec![],
1210                        }]
1211                    }),
1212                }));
1213            }
1214        };
1215
1216        let applicant_nonce_attr_value = match unsafe {
1217            crate::util::cvt_p(X509_ATTRIBUTE_get0_data(
1218                applicant_nonce_attr, 0, openssl_sys::V_ASN1_OCTET_STRING, std::ptr::null()
1219            ))
1220        } {
1221            Ok(v) => unsafe {
1222                openssl::asn1::Asn1StringRef::from_ptr(v as *mut openssl_sys::ASN1_STRING)
1223            },
1224            Err(_) => {
1225                return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1226                    valid: false,
1227                    error: Some(crate::cert_order::ErrorResponse {
1228                        errors: vec![crate::cert_order::Error {
1229                            error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1230                            title: "Invalid CSR".to_string(),
1231                            detail: "Invalid data type for applicant nonce attribute".to_string(),
1232                            status: 400,
1233                            identifier: req.identifier,
1234                            instance: None,
1235                            sub_problems: vec![],
1236                        }]
1237                    }),
1238                }));
1239            }
1240        };
1241
1242        if applicant_nonce_attr_value.len() < 8 {
1243            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1244                valid: false,
1245                error: Some(crate::cert_order::ErrorResponse {
1246                    errors: vec![crate::cert_order::Error {
1247                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1248                        title: "Invalid CSR".to_string(),
1249                        detail: "Applicant nonce attribute does not contain at least 64 bits of entropy".to_string(),
1250                        status: 400,
1251                        identifier: req.identifier,
1252                        instance: None,
1253                        sub_problems: vec![],
1254                    }]
1255                }),
1256            }));
1257        }
1258
1259        if ca_nonce_attr_value.as_slice() != req.ca_nonce {
1260            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1261                valid: false,
1262                error: Some(crate::cert_order::ErrorResponse {
1263                    errors: vec![crate::cert_order::Error {
1264                        error_type: crate::cert_order::ErrorType::IncorrectResponseError.into(),
1265                        title: "Invalid CSR".to_string(),
1266                        detail: "CA nonce attribute does not CA provided value".to_string(),
1267                        status: 400,
1268                        identifier: req.identifier,
1269                        instance: None,
1270                        sub_problems: vec![],
1271                    }]
1272                }),
1273            }));
1274        } else {
1275            return Ok(tonic::Response::new(crate::cert_order::ValidationResult {
1276                valid: true,
1277                error: None
1278            }));
1279        }
1280    }
1281}
1282
1283#[allow(non_camel_case_types)]
1284enum X509_ATTRIBUTE {}
1285
1286extern "C" {
1287    fn X509v3_get_ext_by_OBJ(
1288        x: *const openssl_sys::stack_st_X509_EXTENSION,
1289        obj: *const openssl_sys::ASN1_OBJECT,
1290        lastpos: libc::c_int,
1291    ) -> libc::c_int;
1292
1293    fn d2i_ASN1_OCTET_STRING(
1294        a: *mut *mut openssl_sys::ASN1_OCTET_STRING,
1295        pp: *mut *const libc::c_uchar,
1296        length: libc::c_long,
1297    ) -> *mut openssl_sys::ASN1_OCTET_STRING;
1298
1299    fn X509_REQ_get_attr_by_OBJ(
1300        req: *const openssl_sys::X509_REQ,
1301        obj: *const openssl_sys::ASN1_OBJECT,
1302        start_after: libc::c_int,
1303    ) -> libc::c_int;
1304
1305
1306    fn X509_REQ_get_attr(
1307        req: *const openssl_sys::X509_REQ, index: libc::c_int
1308    ) -> *const X509_ATTRIBUTE;
1309
1310    fn X509_ATTRIBUTE_count(attr: *const X509_ATTRIBUTE) -> libc::c_int;
1311
1312    fn X509_ATTRIBUTE_get0_data(
1313        attr: *const X509_ATTRIBUTE,
1314        index: libc::c_int,
1315        data_type: libc::c_int,
1316        data: *const libc::c_void
1317    ) -> *mut libc::c_void;
1318}