xref: /webrtc/dtls/src/flight/flight5.rs (revision 03a147ee)
1 use super::flight3::*;
2 use super::*;
3 use crate::change_cipher_spec::ChangeCipherSpec;
4 use crate::content::*;
5 use crate::crypto::*;
6 use crate::curve::named_curve::*;
7 use crate::curve::*;
8 use crate::error::Error;
9 use crate::handshake::handshake_message_certificate::*;
10 use crate::handshake::handshake_message_certificate_verify::*;
11 use crate::handshake::handshake_message_client_key_exchange::*;
12 use crate::handshake::handshake_message_finished::*;
13 use crate::handshake::handshake_message_server_key_exchange::*;
14 use crate::handshake::*;
15 use crate::prf::*;
16 use crate::record_layer::record_layer_header::*;
17 use crate::record_layer::*;
18 use crate::signature_hash_algorithm::*;
19 
20 use async_trait::async_trait;
21 use std::fmt;
22 use std::io::{BufReader, BufWriter};
23 
24 #[derive(Debug, PartialEq)]
25 pub(crate) struct Flight5;
26 
27 impl fmt::Display for Flight5 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result28     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29         write!(f, "Flight 5")
30     }
31 }
32 
33 #[async_trait]
34 impl Flight for Flight5 {
is_last_recv_flight(&self) -> bool35     fn is_last_recv_flight(&self) -> bool {
36         true
37     }
38 
parse( &self, _tx: &mut mpsc::Sender<mpsc::Sender<()>>, state: &mut State, cache: &HandshakeCache, cfg: &HandshakeConfig, ) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)>39     async fn parse(
40         &self,
41         _tx: &mut mpsc::Sender<mpsc::Sender<()>>,
42         state: &mut State,
43         cache: &HandshakeCache,
44         cfg: &HandshakeConfig,
45     ) -> Result<Box<dyn Flight + Send + Sync>, (Option<Alert>, Option<Error>)> {
46         let (_seq, msgs) = match cache
47             .full_pull_map(
48                 state.handshake_recv_sequence,
49                 &[HandshakeCachePullRule {
50                     typ: HandshakeType::Finished,
51                     epoch: cfg.initial_epoch + 1,
52                     is_client: false,
53                     optional: false,
54                 }],
55             )
56             .await
57         {
58             Ok((seq, msgs)) => (seq, msgs),
59             Err(_) => return Err((None, None)),
60         };
61 
62         let finished =
63             if let Some(HandshakeMessage::Finished(h)) = msgs.get(&HandshakeType::Finished) {
64                 h
65             } else {
66                 return Err((
67                     Some(Alert {
68                         alert_level: AlertLevel::Fatal,
69                         alert_description: AlertDescription::InternalError,
70                     }),
71                     None,
72                 ));
73             };
74 
75         let plain_text = cache
76             .pull_and_merge(&[
77                 HandshakeCachePullRule {
78                     typ: HandshakeType::ClientHello,
79                     epoch: cfg.initial_epoch,
80                     is_client: true,
81                     optional: false,
82                 },
83                 HandshakeCachePullRule {
84                     typ: HandshakeType::ServerHello,
85                     epoch: cfg.initial_epoch,
86                     is_client: false,
87                     optional: false,
88                 },
89                 HandshakeCachePullRule {
90                     typ: HandshakeType::Certificate,
91                     epoch: cfg.initial_epoch,
92                     is_client: false,
93                     optional: false,
94                 },
95                 HandshakeCachePullRule {
96                     typ: HandshakeType::ServerKeyExchange,
97                     epoch: cfg.initial_epoch,
98                     is_client: false,
99                     optional: false,
100                 },
101                 HandshakeCachePullRule {
102                     typ: HandshakeType::CertificateRequest,
103                     epoch: cfg.initial_epoch,
104                     is_client: false,
105                     optional: false,
106                 },
107                 HandshakeCachePullRule {
108                     typ: HandshakeType::ServerHelloDone,
109                     epoch: cfg.initial_epoch,
110                     is_client: false,
111                     optional: false,
112                 },
113                 HandshakeCachePullRule {
114                     typ: HandshakeType::Certificate,
115                     epoch: cfg.initial_epoch,
116                     is_client: true,
117                     optional: false,
118                 },
119                 HandshakeCachePullRule {
120                     typ: HandshakeType::ClientKeyExchange,
121                     epoch: cfg.initial_epoch,
122                     is_client: true,
123                     optional: false,
124                 },
125                 HandshakeCachePullRule {
126                     typ: HandshakeType::CertificateVerify,
127                     epoch: cfg.initial_epoch,
128                     is_client: true,
129                     optional: false,
130                 },
131                 HandshakeCachePullRule {
132                     typ: HandshakeType::Finished,
133                     epoch: cfg.initial_epoch + 1,
134                     is_client: true,
135                     optional: false,
136                 },
137             ])
138             .await;
139 
140         {
141             let cipher_suite = state.cipher_suite.lock().await;
142             if let Some(cipher_suite) = &*cipher_suite {
143                 let expected_verify_data = match prf_verify_data_server(
144                     &state.master_secret,
145                     &plain_text,
146                     cipher_suite.hash_func(),
147                 ) {
148                     Ok(d) => d,
149                     Err(err) => {
150                         return Err((
151                             Some(Alert {
152                                 alert_level: AlertLevel::Fatal,
153                                 alert_description: AlertDescription::InsufficientSecurity,
154                             }),
155                             Some(err),
156                         ))
157                     }
158                 };
159 
160                 if expected_verify_data != finished.verify_data {
161                     return Err((
162                         Some(Alert {
163                             alert_level: AlertLevel::Fatal,
164                             alert_description: AlertDescription::HandshakeFailure,
165                         }),
166                         Some(Error::ErrVerifyDataMismatch),
167                     ));
168                 }
169             }
170         }
171 
172         Ok(Box::new(Flight5 {}))
173     }
174 
generate( &self, state: &mut State, cache: &HandshakeCache, cfg: &HandshakeConfig, ) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)>175     async fn generate(
176         &self,
177         state: &mut State,
178         cache: &HandshakeCache,
179         cfg: &HandshakeConfig,
180     ) -> Result<Vec<Packet>, (Option<Alert>, Option<Error>)> {
181         let certificate = if !cfg.local_certificates.is_empty() {
182             let cert = match cfg.get_certificate(&cfg.server_name) {
183                 Ok(cert) => cert,
184                 Err(err) => {
185                     return Err((
186                         Some(Alert {
187                             alert_level: AlertLevel::Fatal,
188                             alert_description: AlertDescription::HandshakeFailure,
189                         }),
190                         Some(err),
191                     ))
192                 }
193             };
194             Some(cert)
195         } else {
196             None
197         };
198 
199         let mut pkts = vec![];
200 
201         if state.remote_requested_certificate {
202             pkts.push(Packet {
203                 record: RecordLayer::new(
204                     PROTOCOL_VERSION1_2,
205                     0,
206                     Content::Handshake(Handshake::new(HandshakeMessage::Certificate(
207                         HandshakeMessageCertificate {
208                             certificate: if let Some(cert) = &certificate {
209                                 cert.certificate.iter().map(|x| x.0.clone()).collect()
210                             } else {
211                                 vec![]
212                             },
213                         },
214                     ))),
215                 ),
216                 should_encrypt: false,
217                 reset_local_sequence_number: false,
218             });
219         }
220 
221         let mut client_key_exchange = HandshakeMessageClientKeyExchange {
222             identity_hint: vec![],
223             public_key: vec![],
224         };
225         if cfg.local_psk_callback.is_none() {
226             if let Some(local_keypair) = &state.local_keypair {
227                 client_key_exchange.public_key = local_keypair.public_key.clone();
228             }
229         } else if let Some(local_psk_identity_hint) = &cfg.local_psk_identity_hint {
230             client_key_exchange.identity_hint = local_psk_identity_hint.clone();
231         }
232 
233         pkts.push(Packet {
234             record: RecordLayer::new(
235                 PROTOCOL_VERSION1_2,
236                 0,
237                 Content::Handshake(Handshake::new(HandshakeMessage::ClientKeyExchange(
238                     client_key_exchange,
239                 ))),
240             ),
241             should_encrypt: false,
242             reset_local_sequence_number: false,
243         });
244 
245         let server_key_exchange_data = cache
246             .pull_and_merge(&[HandshakeCachePullRule {
247                 typ: HandshakeType::ServerKeyExchange,
248                 epoch: cfg.initial_epoch,
249                 is_client: false,
250                 optional: false,
251             }])
252             .await;
253 
254         let mut server_key_exchange = HandshakeMessageServerKeyExchange {
255             identity_hint: vec![],
256             elliptic_curve_type: EllipticCurveType::Unsupported,
257             named_curve: NamedCurve::Unsupported,
258             public_key: vec![],
259             algorithm: SignatureHashAlgorithm {
260                 hash: HashAlgorithm::Unsupported,
261                 signature: SignatureAlgorithm::Unsupported,
262             },
263             signature: vec![],
264         };
265 
266         // handshakeMessageServerKeyExchange is optional for PSK
267         if server_key_exchange_data.is_empty() {
268             if let Err((alert, err)) = handle_server_key_exchange(state, cfg, &server_key_exchange)
269             {
270                 return Err((alert, err));
271             }
272         } else {
273             let mut reader = BufReader::new(server_key_exchange_data.as_slice());
274             let raw_handshake = match Handshake::unmarshal(&mut reader) {
275                 Ok(h) => h,
276                 Err(err) => {
277                     return Err((
278                         Some(Alert {
279                             alert_level: AlertLevel::Fatal,
280                             alert_description: AlertDescription::UnexpectedMessage,
281                         }),
282                         Some(err),
283                     ))
284                 }
285             };
286 
287             match raw_handshake.handshake_message {
288                 HandshakeMessage::ServerKeyExchange(h) => server_key_exchange = h,
289                 _ => {
290                     return Err((
291                         Some(Alert {
292                             alert_level: AlertLevel::Fatal,
293                             alert_description: AlertDescription::UnexpectedMessage,
294                         }),
295                         Some(Error::ErrInvalidContentType),
296                     ))
297                 }
298             };
299         }
300 
301         // Append not-yet-sent packets
302         let mut merged = vec![];
303         let mut seq_pred = state.handshake_send_sequence as u16;
304         for p in &mut pkts {
305             let h = match &mut p.record.content {
306                 Content::Handshake(h) => h,
307                 _ => {
308                     return Err((
309                         Some(Alert {
310                             alert_level: AlertLevel::Fatal,
311                             alert_description: AlertDescription::InternalError,
312                         }),
313                         Some(Error::ErrInvalidContentType),
314                     ))
315                 }
316             };
317             h.handshake_header.message_sequence = seq_pred;
318             seq_pred += 1;
319 
320             let mut raw = vec![];
321             {
322                 let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
323                 if let Err(err) = h.marshal(&mut writer) {
324                     return Err((
325                         Some(Alert {
326                             alert_level: AlertLevel::Fatal,
327                             alert_description: AlertDescription::InternalError,
328                         }),
329                         Some(err),
330                     ));
331                 }
332             }
333 
334             merged.extend_from_slice(&raw);
335         }
336 
337         if let Err((alert, err)) =
338             initalize_cipher_suite(state, cache, cfg, &server_key_exchange, &merged).await
339         {
340             return Err((alert, err));
341         }
342 
343         // If the client has sent a certificate with signing ability, a digitally-signed
344         // CertificateVerify message is sent to explicitly verify possession of the
345         // private key in the certificate.
346         if state.remote_requested_certificate && !cfg.local_certificates.is_empty() {
347             let mut plain_text = cache
348                 .pull_and_merge(&[
349                     HandshakeCachePullRule {
350                         typ: HandshakeType::ClientHello,
351                         epoch: cfg.initial_epoch,
352                         is_client: true,
353                         optional: false,
354                     },
355                     HandshakeCachePullRule {
356                         typ: HandshakeType::ServerHello,
357                         epoch: cfg.initial_epoch,
358                         is_client: false,
359                         optional: false,
360                     },
361                     HandshakeCachePullRule {
362                         typ: HandshakeType::Certificate,
363                         epoch: cfg.initial_epoch,
364                         is_client: false,
365                         optional: false,
366                     },
367                     HandshakeCachePullRule {
368                         typ: HandshakeType::ServerKeyExchange,
369                         epoch: cfg.initial_epoch,
370                         is_client: false,
371                         optional: false,
372                     },
373                     HandshakeCachePullRule {
374                         typ: HandshakeType::CertificateRequest,
375                         epoch: cfg.initial_epoch,
376                         is_client: false,
377                         optional: false,
378                     },
379                     HandshakeCachePullRule {
380                         typ: HandshakeType::ServerHelloDone,
381                         epoch: cfg.initial_epoch,
382                         is_client: false,
383                         optional: false,
384                     },
385                     HandshakeCachePullRule {
386                         typ: HandshakeType::Certificate,
387                         epoch: cfg.initial_epoch,
388                         is_client: true,
389                         optional: false,
390                     },
391                     HandshakeCachePullRule {
392                         typ: HandshakeType::ClientKeyExchange,
393                         epoch: cfg.initial_epoch,
394                         is_client: true,
395                         optional: false,
396                     },
397                 ])
398                 .await;
399 
400             plain_text.extend_from_slice(&merged);
401 
402             // Find compatible signature scheme
403             let signature_hash_algo = match select_signature_scheme(
404                 &cfg.local_signature_schemes,
405                 &certificate.as_ref().unwrap().private_key,
406             ) {
407                 Ok(s) => s,
408                 Err(err) => {
409                     return Err((
410                         Some(Alert {
411                             alert_level: AlertLevel::Fatal,
412                             alert_description: AlertDescription::InsufficientSecurity,
413                         }),
414                         Some(err),
415                     ))
416                 }
417             };
418 
419             let cert_verify = match generate_certificate_verify(
420                 &plain_text,
421                 &certificate.as_ref().unwrap().private_key, /*, signature_hash_algo.hash*/
422             ) {
423                 Ok(cert) => cert,
424                 Err(err) => {
425                     return Err((
426                         Some(Alert {
427                             alert_level: AlertLevel::Fatal,
428                             alert_description: AlertDescription::InternalError,
429                         }),
430                         Some(err),
431                     ))
432                 }
433             };
434             state.local_certificates_verify = cert_verify;
435 
436             let mut p = Packet {
437                 record: RecordLayer::new(
438                     PROTOCOL_VERSION1_2,
439                     0,
440                     Content::Handshake(Handshake::new(HandshakeMessage::CertificateVerify(
441                         HandshakeMessageCertificateVerify {
442                             algorithm: signature_hash_algo,
443                             signature: state.local_certificates_verify.clone(),
444                         },
445                     ))),
446                 ),
447                 should_encrypt: false,
448                 reset_local_sequence_number: false,
449             };
450 
451             let h = match &mut p.record.content {
452                 Content::Handshake(h) => h,
453                 _ => {
454                     return Err((
455                         Some(Alert {
456                             alert_level: AlertLevel::Fatal,
457                             alert_description: AlertDescription::InternalError,
458                         }),
459                         Some(Error::ErrInvalidContentType),
460                     ))
461                 }
462             };
463             h.handshake_header.message_sequence = seq_pred;
464 
465             // seqPred++ // this is the last use of seqPred
466 
467             let mut raw = vec![];
468             {
469                 let mut writer = BufWriter::<&mut Vec<u8>>::new(raw.as_mut());
470                 if let Err(err) = h.marshal(&mut writer) {
471                     return Err((
472                         Some(Alert {
473                             alert_level: AlertLevel::Fatal,
474                             alert_description: AlertDescription::InternalError,
475                         }),
476                         Some(err),
477                     ));
478                 }
479             }
480             merged.extend_from_slice(&raw);
481 
482             pkts.push(p);
483         }
484 
485         pkts.push(Packet {
486             record: RecordLayer::new(
487                 PROTOCOL_VERSION1_2,
488                 0,
489                 Content::ChangeCipherSpec(ChangeCipherSpec {}),
490             ),
491             should_encrypt: false,
492             reset_local_sequence_number: false,
493         });
494 
495         if state.local_verify_data.is_empty() {
496             let mut plain_text = cache
497                 .pull_and_merge(&[
498                     HandshakeCachePullRule {
499                         typ: HandshakeType::ClientHello,
500                         epoch: cfg.initial_epoch,
501                         is_client: true,
502                         optional: false,
503                     },
504                     HandshakeCachePullRule {
505                         typ: HandshakeType::ServerHello,
506                         epoch: cfg.initial_epoch,
507                         is_client: false,
508                         optional: false,
509                     },
510                     HandshakeCachePullRule {
511                         typ: HandshakeType::Certificate,
512                         epoch: cfg.initial_epoch,
513                         is_client: false,
514                         optional: false,
515                     },
516                     HandshakeCachePullRule {
517                         typ: HandshakeType::ServerKeyExchange,
518                         epoch: cfg.initial_epoch,
519                         is_client: false,
520                         optional: false,
521                     },
522                     HandshakeCachePullRule {
523                         typ: HandshakeType::CertificateRequest,
524                         epoch: cfg.initial_epoch,
525                         is_client: false,
526                         optional: false,
527                     },
528                     HandshakeCachePullRule {
529                         typ: HandshakeType::ServerHelloDone,
530                         epoch: cfg.initial_epoch,
531                         is_client: false,
532                         optional: false,
533                     },
534                     HandshakeCachePullRule {
535                         typ: HandshakeType::Certificate,
536                         epoch: cfg.initial_epoch,
537                         is_client: true,
538                         optional: false,
539                     },
540                     HandshakeCachePullRule {
541                         typ: HandshakeType::ClientKeyExchange,
542                         epoch: cfg.initial_epoch,
543                         is_client: true,
544                         optional: false,
545                     },
546                     HandshakeCachePullRule {
547                         typ: HandshakeType::CertificateVerify,
548                         epoch: cfg.initial_epoch,
549                         is_client: true,
550                         optional: false,
551                     },
552                     HandshakeCachePullRule {
553                         typ: HandshakeType::Finished,
554                         epoch: cfg.initial_epoch + 1,
555                         is_client: true,
556                         optional: false,
557                     },
558                 ])
559                 .await;
560 
561             plain_text.extend_from_slice(&merged);
562 
563             let cipher_suite = state.cipher_suite.lock().await;
564             if let Some(cipher_suite) = &*cipher_suite {
565                 state.local_verify_data = match prf_verify_data_client(
566                     &state.master_secret,
567                     &plain_text,
568                     cipher_suite.hash_func(),
569                 ) {
570                     Ok(data) => data,
571                     Err(err) => {
572                         return Err((
573                             Some(Alert {
574                                 alert_level: AlertLevel::Fatal,
575                                 alert_description: AlertDescription::InternalError,
576                             }),
577                             Some(err),
578                         ))
579                     }
580                 };
581             }
582         }
583 
584         pkts.push(Packet {
585             record: RecordLayer::new(
586                 PROTOCOL_VERSION1_2,
587                 1,
588                 Content::Handshake(Handshake::new(HandshakeMessage::Finished(
589                     HandshakeMessageFinished {
590                         verify_data: state.local_verify_data.clone(),
591                     },
592                 ))),
593             ),
594             should_encrypt: true,
595             reset_local_sequence_number: true,
596         });
597 
598         Ok(pkts)
599     }
600 }
initalize_cipher_suite( state: &mut State, cache: &HandshakeCache, cfg: &HandshakeConfig, h: &HandshakeMessageServerKeyExchange, sending_plain_text: &[u8], ) -> Result<(), (Option<Alert>, Option<Error>)>601 async fn initalize_cipher_suite(
602     state: &mut State,
603     cache: &HandshakeCache,
604     cfg: &HandshakeConfig,
605     h: &HandshakeMessageServerKeyExchange,
606     sending_plain_text: &[u8],
607 ) -> Result<(), (Option<Alert>, Option<Error>)> {
608     let mut cipher_suite = state.cipher_suite.lock().await;
609 
610     if let Some(cipher_suite) = &*cipher_suite {
611         if cipher_suite.is_initialized() {
612             return Ok(());
613         }
614     }
615 
616     let mut client_random = vec![];
617     {
618         let mut writer = BufWriter::<&mut Vec<u8>>::new(client_random.as_mut());
619         let _ = state.local_random.marshal(&mut writer);
620     }
621     let mut server_random = vec![];
622     {
623         let mut writer = BufWriter::<&mut Vec<u8>>::new(server_random.as_mut());
624         let _ = state.remote_random.marshal(&mut writer);
625     }
626 
627     if let Some(cipher_suite) = &*cipher_suite {
628         if state.extended_master_secret {
629             let session_hash = match cache
630                 .session_hash(
631                     cipher_suite.hash_func(),
632                     cfg.initial_epoch,
633                     sending_plain_text,
634                 )
635                 .await
636             {
637                 Ok(s) => s,
638                 Err(err) => {
639                     return Err((
640                         Some(Alert {
641                             alert_level: AlertLevel::Fatal,
642                             alert_description: AlertDescription::InternalError,
643                         }),
644                         Some(err),
645                     ))
646                 }
647             };
648 
649             state.master_secret = match prf_extended_master_secret(
650                 &state.pre_master_secret,
651                 &session_hash,
652                 cipher_suite.hash_func(),
653             ) {
654                 Ok(m) => m,
655                 Err(err) => {
656                     return Err((
657                         Some(Alert {
658                             alert_level: AlertLevel::Fatal,
659                             alert_description: AlertDescription::IllegalParameter,
660                         }),
661                         Some(err),
662                     ))
663                 }
664             };
665         } else {
666             state.master_secret = match prf_master_secret(
667                 &state.pre_master_secret,
668                 &client_random,
669                 &server_random,
670                 cipher_suite.hash_func(),
671             ) {
672                 Ok(m) => m,
673                 Err(err) => {
674                     return Err((
675                         Some(Alert {
676                             alert_level: AlertLevel::Fatal,
677                             alert_description: AlertDescription::InternalError,
678                         }),
679                         Some(err),
680                     ))
681                 }
682             };
683         }
684     }
685 
686     if cfg.local_psk_callback.is_none() {
687         // Verify that the pair of hash algorithm and signiture is listed.
688         let mut valid_signature_scheme = false;
689         for ss in &cfg.local_signature_schemes {
690             if ss.hash == h.algorithm.hash && ss.signature == h.algorithm.signature {
691                 valid_signature_scheme = true;
692                 break;
693             }
694         }
695         if !valid_signature_scheme {
696             return Err((
697                 Some(Alert {
698                     alert_level: AlertLevel::Fatal,
699                     alert_description: AlertDescription::InsufficientSecurity,
700                 }),
701                 Some(Error::ErrNoAvailableSignatureSchemes),
702             ));
703         }
704 
705         let expected_msg =
706             value_key_message(&client_random, &server_random, &h.public_key, h.named_curve);
707         if let Err(err) = verify_key_signature(
708             &expected_msg,
709             &h.algorithm,
710             &h.signature,
711             &state.peer_certificates,
712             cfg.insecure_verification,
713         ) {
714             return Err((
715                 Some(Alert {
716                     alert_level: AlertLevel::Fatal,
717                     alert_description: AlertDescription::BadCertificate,
718                 }),
719                 Some(err),
720             ));
721         }
722 
723         let mut chains = vec![];
724         if !cfg.insecure_skip_verify {
725             chains = match verify_server_cert(
726                 &state.peer_certificates,
727                 &cfg.server_cert_verifier,
728                 &cfg.roots_cas,
729                 &cfg.server_name,
730             ) {
731                 Ok(chains) => chains,
732                 Err(err) => {
733                     return Err((
734                         Some(Alert {
735                             alert_level: AlertLevel::Fatal,
736                             alert_description: AlertDescription::BadCertificate,
737                         }),
738                         Some(err),
739                     ))
740                 }
741             }
742         }
743         if let Some(verify_peer_certificate) = &cfg.verify_peer_certificate {
744             if let Err(err) = verify_peer_certificate(&state.peer_certificates, &chains) {
745                 return Err((
746                     Some(Alert {
747                         alert_level: AlertLevel::Fatal,
748                         alert_description: AlertDescription::BadCertificate,
749                     }),
750                     Some(err),
751                 ));
752             }
753         }
754     }
755 
756     if let Some(cipher_suite) = &mut *cipher_suite {
757         if let Err(err) =
758             cipher_suite.init(&state.master_secret, &client_random, &server_random, true)
759         {
760             return Err((
761                 Some(Alert {
762                     alert_level: AlertLevel::Fatal,
763                     alert_description: AlertDescription::InternalError,
764                 }),
765                 Some(err),
766             ));
767         }
768     }
769 
770     Ok(())
771 }
772