1use crate::cipher::{Iv, IvLen};
2use crate::error::TLSError;
3use crate::msgs::base::PayloadU8;
4use crate::KeyLog;
5use ring::{
7 aead, digest,
8 hkdf::{self, KeyType as _},
9 hmac,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq)]
14enum SecretKind {
15 ResumptionPSKBinderKey,
16 ClientEarlyTrafficSecret,
17 ClientHandshakeTrafficSecret,
18 ServerHandshakeTrafficSecret,
19 ClientApplicationTrafficSecret,
20 ServerApplicationTrafficSecret,
21 ExporterMasterSecret,
22 ResumptionMasterSecret,
23 DerivedSecret,
24}
25
26impl SecretKind {
27 fn to_bytes(self) -> &'static [u8] {
28 match self {
29 SecretKind::ResumptionPSKBinderKey => b"res binder",
30 SecretKind::ClientEarlyTrafficSecret => b"c e traffic",
31 SecretKind::ClientHandshakeTrafficSecret => b"c hs traffic",
32 SecretKind::ServerHandshakeTrafficSecret => b"s hs traffic",
33 SecretKind::ClientApplicationTrafficSecret => b"c ap traffic",
34 SecretKind::ServerApplicationTrafficSecret => b"s ap traffic",
35 SecretKind::ExporterMasterSecret => b"exp master",
36 SecretKind::ResumptionMasterSecret => b"res master",
37 SecretKind::DerivedSecret => b"derived",
38 }
39 }
40
41 fn log_label(self) -> Option<&'static str> {
42 use self::SecretKind::*;
43 Some(match self {
44 ClientEarlyTrafficSecret => "CLIENT_EARLY_TRAFFIC_SECRET",
45 ClientHandshakeTrafficSecret => "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
46 ServerHandshakeTrafficSecret => "SERVER_HANDSHAKE_TRAFFIC_SECRET",
47 ClientApplicationTrafficSecret => "CLIENT_TRAFFIC_SECRET_0",
48 ServerApplicationTrafficSecret => "SERVER_TRAFFIC_SECRET_0",
49 ExporterMasterSecret => "EXPORTER_SECRET",
50 _ => {
51 return None;
52 }
53 })
54 }
55}
56
57struct KeySchedule {
61 current: hkdf::Prk,
62 algorithm: ring::hkdf::Algorithm,
63}
64
65pub struct KeyScheduleEarly {
73 ks: KeySchedule,
74}
75
76impl KeyScheduleEarly {
77 pub fn new(algorithm: hkdf::Algorithm, secret: &[u8]) -> KeyScheduleEarly {
78 KeyScheduleEarly {
79 ks: KeySchedule::new(algorithm, secret),
80 }
81 }
82
83 pub fn client_early_traffic_secret(
84 &self,
85 hs_hash: &[u8],
86 key_log: &dyn KeyLog,
87 client_random: &[u8; 32],
88 ) -> hkdf::Prk {
89 self.ks.derive_logged_secret(
90 SecretKind::ClientEarlyTrafficSecret,
91 hs_hash,
92 key_log,
93 client_random,
94 )
95 }
96
97 pub fn resumption_psk_binder_key_and_sign_verify_data(&self, hs_hash: &[u8]) -> Vec<u8> {
98 let resumption_psk_binder_key = self
99 .ks
100 .derive_for_empty_hash(SecretKind::ResumptionPSKBinderKey);
101 self.ks
102 .sign_verify_data(&resumption_psk_binder_key, hs_hash)
103 }
104
105 pub fn into_handshake(mut self, secret: &[u8]) -> KeyScheduleHandshake {
106 self.ks.input_secret(secret);
107 KeyScheduleHandshake {
108 ks: self.ks,
109 current_client_traffic_secret: None,
110 current_server_traffic_secret: None,
111 }
112 }
113}
114
115pub struct KeyScheduleNonSecret {
118 ks: KeySchedule,
119}
120
121impl KeyScheduleNonSecret {
122 pub fn new(algorithm: hkdf::Algorithm) -> KeyScheduleNonSecret {
123 KeyScheduleNonSecret {
124 ks: KeySchedule::new_with_empty_secret(algorithm),
125 }
126 }
127
128 pub fn into_handshake(mut self, secret: &[u8]) -> KeyScheduleHandshake {
129 self.ks.input_secret(secret);
130 KeyScheduleHandshake {
131 ks: self.ks,
132 current_client_traffic_secret: None,
133 current_server_traffic_secret: None,
134 }
135 }
136}
137
138pub struct KeyScheduleHandshake {
140 ks: KeySchedule,
141 current_client_traffic_secret: Option<hkdf::Prk>,
142 current_server_traffic_secret: Option<hkdf::Prk>,
143}
144
145impl KeyScheduleHandshake {
146 pub fn client_handshake_traffic_secret(
147 &mut self,
148 hs_hash: &[u8],
149 key_log: &dyn KeyLog,
150 client_random: &[u8; 32],
151 ) -> hkdf::Prk {
152 let secret = self.ks.derive_logged_secret(
153 SecretKind::ClientHandshakeTrafficSecret,
154 hs_hash,
155 key_log,
156 client_random,
157 );
158 self.current_client_traffic_secret = Some(secret.clone());
159 secret
160 }
161
162 pub fn server_handshake_traffic_secret(
163 &mut self,
164 hs_hash: &[u8],
165 key_log: &dyn KeyLog,
166 client_random: &[u8; 32],
167 ) -> hkdf::Prk {
168 let secret = self.ks.derive_logged_secret(
169 SecretKind::ServerHandshakeTrafficSecret,
170 hs_hash,
171 key_log,
172 client_random,
173 );
174 self.current_server_traffic_secret = Some(secret.clone());
175 secret
176 }
177
178 pub fn sign_server_finish(&self, hs_hash: &[u8]) -> Vec<u8> {
179 self.ks.sign_finish(
180 self.current_server_traffic_secret
181 .as_ref()
182 .unwrap(),
183 hs_hash,
184 )
185 }
186
187 pub fn into_traffic_with_client_finished_pending(
188 mut self,
189 ) -> KeyScheduleTrafficWithClientFinishedPending {
190 self.ks.input_empty();
191 KeyScheduleTrafficWithClientFinishedPending {
192 ks: self.ks,
193 handshake_client_traffic_secret: self
194 .current_client_traffic_secret
195 .unwrap(),
196 current_client_traffic_secret: None,
197 current_server_traffic_secret: None,
198 current_exporter_secret: None,
199 }
200 }
201}
202
203pub struct KeyScheduleTrafficWithClientFinishedPending {
206 ks: KeySchedule,
207 handshake_client_traffic_secret: hkdf::Prk,
208 current_client_traffic_secret: Option<hkdf::Prk>,
209 current_server_traffic_secret: Option<hkdf::Prk>,
210 current_exporter_secret: Option<hkdf::Prk>,
211}
212
213impl KeyScheduleTrafficWithClientFinishedPending {
214 pub fn sign_client_finish(&self, hs_hash: &[u8]) -> Vec<u8> {
215 self.ks
216 .sign_finish(&self.handshake_client_traffic_secret, hs_hash)
217 }
218
219 pub fn server_application_traffic_secret(
220 &mut self,
221 hs_hash: &[u8],
222 key_log: &dyn KeyLog,
223 client_random: &[u8; 32],
224 ) -> hkdf::Prk {
225 let secret = self.ks.derive_logged_secret(
226 SecretKind::ServerApplicationTrafficSecret,
227 hs_hash,
228 key_log,
229 client_random,
230 );
231 self.current_server_traffic_secret = Some(secret.clone());
232 secret
233 }
234
235 pub fn client_application_traffic_secret(
236 &mut self,
237 hs_hash: &[u8],
238 key_log: &dyn KeyLog,
239 client_random: &[u8; 32],
240 ) -> hkdf::Prk {
241 let secret = self.ks.derive_logged_secret(
242 SecretKind::ClientApplicationTrafficSecret,
243 hs_hash,
244 key_log,
245 client_random,
246 );
247 self.current_client_traffic_secret = Some(secret.clone());
248 secret
249 }
250
251 pub fn exporter_master_secret(
252 &mut self,
253 hs_hash: &[u8],
254 key_log: &dyn KeyLog,
255 client_random: &[u8; 32],
256 ) {
257 let secret = self.ks.derive_logged_secret(
258 SecretKind::ExporterMasterSecret,
259 hs_hash,
260 key_log,
261 client_random,
262 );
263 self.current_exporter_secret = Some(secret);
264 }
265
266 pub fn into_traffic(self) -> KeyScheduleTraffic {
267 KeyScheduleTraffic {
268 ks: self.ks,
269 current_client_traffic_secret: self
270 .current_client_traffic_secret
271 .unwrap(),
272 current_server_traffic_secret: self
273 .current_server_traffic_secret
274 .unwrap(),
275 current_exporter_secret: self.current_exporter_secret.unwrap(),
276 }
277 }
278}
279
280pub struct KeyScheduleTraffic {
283 ks: KeySchedule,
284 current_client_traffic_secret: hkdf::Prk,
285 current_server_traffic_secret: hkdf::Prk,
286 current_exporter_secret: hkdf::Prk,
287}
288
289impl KeyScheduleTraffic {
290 pub fn next_server_application_traffic_secret(&mut self) -> hkdf::Prk {
291 let secret = self
292 .ks
293 .derive_next(&self.current_server_traffic_secret);
294 self.current_server_traffic_secret = secret.clone();
295 secret
296 }
297
298 pub fn next_client_application_traffic_secret(&mut self) -> hkdf::Prk {
299 let secret = self
300 .ks
301 .derive_next(&self.current_client_traffic_secret);
302 self.current_client_traffic_secret = secret.clone();
303 secret
304 }
305
306 pub fn resumption_master_secret_and_derive_ticket_psk(
307 &self,
308 hs_hash: &[u8],
309 nonce: &[u8],
310 ) -> Vec<u8> {
311 let resumption_master_secret = self.ks.derive(
312 self.ks.algorithm(),
313 SecretKind::ResumptionMasterSecret,
314 hs_hash,
315 );
316 self.ks
317 .derive_ticket_psk(&resumption_master_secret, nonce)
318 }
319
320 pub fn export_keying_material(
321 &self,
322 out: &mut [u8],
323 label: &[u8],
324 context: Option<&[u8]>,
325 ) -> Result<(), TLSError> {
326 self.ks
327 .export_keying_material(&self.current_exporter_secret, out, label, context)
328 }
329}
330
331impl KeySchedule {
332 fn new(algorithm: hkdf::Algorithm, secret: &[u8]) -> KeySchedule {
333 let zeroes = [0u8; digest::MAX_OUTPUT_LEN];
334 let zeroes = &zeroes[..algorithm.len()];
335 let salt = hkdf::Salt::new(algorithm, &zeroes);
336 KeySchedule {
337 current: salt.extract(secret),
338 algorithm,
339 }
340 }
341
342 #[inline]
343 fn algorithm(&self) -> hkdf::Algorithm {
344 self.algorithm
345 }
346
347 fn new_with_empty_secret(algorithm: hkdf::Algorithm) -> KeySchedule {
348 let zeroes = [0u8; digest::MAX_OUTPUT_LEN];
349 Self::new(algorithm, &zeroes[..algorithm.len()])
350 }
351
352 fn input_empty(&mut self) {
354 let zeroes = [0u8; digest::MAX_OUTPUT_LEN];
355 self.input_secret(&zeroes[..self.algorithm.len()]);
356 }
357
358 fn input_secret(&mut self, secret: &[u8]) {
360 let salt: hkdf::Salt = self.derive_for_empty_hash(SecretKind::DerivedSecret);
361 self.current = salt.extract(secret);
362 }
363
364 fn derive<T, L>(&self, key_type: L, kind: SecretKind, hs_hash: &[u8]) -> T
366 where
367 T: for<'a> From<hkdf::Okm<'a, L>>,
368 L: hkdf::KeyType,
369 {
370 hkdf_expand(&self.current, key_type, kind.to_bytes(), hs_hash)
371 }
372
373 fn derive_logged_secret(
374 &self,
375 kind: SecretKind,
376 hs_hash: &[u8],
377 key_log: &dyn KeyLog,
378 client_random: &[u8; 32],
379 ) -> hkdf::Prk {
380 let log_label = kind
381 .log_label()
382 .expect("not a loggable secret");
383 if key_log.will_log(log_label) {
384 let secret = self
385 .derive::<PayloadU8, _>(PayloadU8Len(self.algorithm.len()), kind, hs_hash)
386 .into_inner();
387 key_log.log(log_label, client_random, &secret);
388 }
389 self.derive(self.algorithm, kind, hs_hash)
390 }
391
392 fn derive_for_empty_hash<T>(&self, kind: SecretKind) -> T
397 where
398 T: for<'a> From<hkdf::Okm<'a, hkdf::Algorithm>>,
399 {
400 let digest_alg = self
401 .algorithm
402 .hmac_algorithm()
403 .digest_algorithm();
404 let empty_hash = digest::digest(digest_alg, &[]);
405 self.derive(self.algorithm, kind, empty_hash.as_ref())
406 }
407
408 fn sign_finish(&self, base_key: &hkdf::Prk, hs_hash: &[u8]) -> Vec<u8> {
411 self.sign_verify_data(base_key, hs_hash)
412 }
413
414 fn sign_verify_data(&self, base_key: &hkdf::Prk, hs_hash: &[u8]) -> Vec<u8> {
417 let hmac_alg = self.algorithm.hmac_algorithm();
418 let hmac_key = hkdf_expand(base_key, hmac_alg, b"finished", &[]);
419 hmac::sign(&hmac_key, hs_hash)
420 .as_ref()
421 .to_vec()
422 }
423
424 fn derive_next(&self, base_key: &hkdf::Prk) -> hkdf::Prk {
426 hkdf_expand(&base_key, self.algorithm, b"traffic upd", &[])
427 }
428
429 fn derive_ticket_psk(&self, rms: &hkdf::Prk, nonce: &[u8]) -> Vec<u8> {
432 let payload: PayloadU8 = hkdf_expand(
433 rms,
434 PayloadU8Len(self.algorithm.len()),
435 b"resumption",
436 nonce,
437 );
438 payload.into_inner()
439 }
440
441 fn export_keying_material(
442 &self,
443 current_exporter_secret: &hkdf::Prk,
444 out: &mut [u8],
445 label: &[u8],
446 context: Option<&[u8]>,
447 ) -> Result<(), TLSError> {
448 let digest_alg = self
449 .algorithm
450 .hmac_algorithm()
451 .digest_algorithm();
452
453 let h_empty = digest::digest(digest_alg, &[]);
454 let secret: hkdf::Prk = hkdf_expand(
455 current_exporter_secret,
456 self.algorithm,
457 label,
458 h_empty.as_ref(),
459 );
460
461 let h_context = digest::digest(digest_alg, context.unwrap_or(&[]));
462
463 hkdf_expand_info(
465 &secret,
466 PayloadU8Len(out.len()),
467 b"exporter",
468 h_context.as_ref(),
469 |okm| okm.fill(out),
470 )
471 .map_err(|_| TLSError::General("exporting too much".to_string()))
472 }
473}
474
475pub(crate) fn hkdf_expand<T, L>(secret: &hkdf::Prk, key_type: L, label: &[u8], context: &[u8]) -> T
476where
477 T: for<'a> From<hkdf::Okm<'a, L>>,
478 L: hkdf::KeyType,
479{
480 hkdf_expand_info(secret, key_type, label, context, |okm| okm.into())
481}
482
483fn hkdf_expand_info<F, T, L>(
484 secret: &hkdf::Prk,
485 key_type: L,
486 label: &[u8],
487 context: &[u8],
488 f: F,
489) -> T
490where
491 F: for<'b> FnOnce(hkdf::Okm<'b, L>) -> T,
492 L: hkdf::KeyType,
493{
494 const LABEL_PREFIX: &[u8] = b"tls13 ";
495
496 let output_len = u16::to_be_bytes(key_type.len() as u16);
497 let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8);
498 let context_len = u8::to_be_bytes(context.len() as u8);
499
500 let info = &[
501 &output_len[..],
502 &label_len[..],
503 LABEL_PREFIX,
504 label,
505 &context_len[..],
506 context,
507 ];
508 let okm = secret.expand(info, key_type).unwrap();
509
510 f(okm)
511}
512
513pub(crate) struct PayloadU8Len(pub(crate) usize);
514impl hkdf::KeyType for PayloadU8Len {
515 fn len(&self) -> usize {
516 self.0
517 }
518}
519
520impl From<hkdf::Okm<'_, PayloadU8Len>> for PayloadU8 {
521 fn from(okm: hkdf::Okm<PayloadU8Len>) -> Self {
522 let mut r = vec![0u8; okm.len().0];
523 okm.fill(&mut r[..]).unwrap();
524 PayloadU8::new(r)
525 }
526}
527
528pub fn derive_traffic_key(
529 secret: &hkdf::Prk,
530 aead_algorithm: &'static aead::Algorithm,
531) -> aead::UnboundKey {
532 hkdf_expand(secret, aead_algorithm, b"key", &[])
533}
534
535pub(crate) fn derive_traffic_iv(secret: &hkdf::Prk) -> Iv {
536 hkdf_expand(secret, IvLen, b"iv", &[])
537}
538
539#[cfg(test)]
540mod test {
541 use super::{derive_traffic_iv, derive_traffic_key, KeySchedule, SecretKind};
542 use crate::KeyLog;
543 use ring::{aead, hkdf};
544
545 #[test]
546 fn test_vectors() {
547 let hs_start_hash = [
549 0xec, 0x14, 0x7a, 0x06, 0xde, 0xa3, 0xc8, 0x84, 0x6c, 0x02, 0xb2, 0x23, 0x8e, 0x41,
550 0xbd, 0xdc, 0x9d, 0x89, 0xf9, 0xae, 0xa1, 0x7b, 0x5e, 0xfd, 0x4d, 0x74, 0x82, 0xaf,
551 0x75, 0x88, 0x1c, 0x0a,
552 ];
553
554 let hs_full_hash = [
555 0x75, 0x1a, 0x3d, 0x4a, 0x14, 0xdf, 0xab, 0xeb, 0x68, 0xe9, 0x2c, 0xa5, 0x91, 0x8e,
556 0x24, 0x08, 0xb9, 0xbc, 0xb0, 0x74, 0x89, 0x82, 0xec, 0x9c, 0x32, 0x30, 0xac, 0x30,
557 0xbb, 0xeb, 0x23, 0xe2,
558 ];
559
560 let ecdhe_secret = [
561 0xe7, 0xb8, 0xfe, 0xf8, 0x90, 0x3b, 0x52, 0x0c, 0xb9, 0xa1, 0x89, 0x71, 0xb6, 0x9d,
562 0xd4, 0x5d, 0xca, 0x53, 0xce, 0x2f, 0x12, 0xbf, 0x3b, 0xef, 0x93, 0x15, 0xe3, 0x12,
563 0x71, 0xdf, 0x4b, 0x40,
564 ];
565
566 let client_hts = [
567 0x61, 0x7b, 0x35, 0x07, 0x6b, 0x9d, 0x0e, 0x08, 0xcf, 0x73, 0x1d, 0x94, 0xa8, 0x66,
568 0x14, 0x78, 0x41, 0x09, 0xef, 0x25, 0x55, 0x51, 0x92, 0x1d, 0xd4, 0x6e, 0x04, 0x01,
569 0x35, 0xcf, 0x46, 0xab,
570 ];
571
572 let client_hts_key = [
573 0x62, 0xd0, 0xdd, 0x00, 0xf6, 0x96, 0x19, 0xd3, 0xb8, 0x19, 0x3a, 0xb4, 0xa0, 0x95,
574 0x85, 0xa7,
575 ];
576
577 let client_hts_iv = [
578 0xff, 0xf7, 0x5d, 0xf5, 0xad, 0x35, 0xd5, 0xcb, 0x3c, 0x53, 0xf3, 0xa9,
579 ];
580
581 let server_hts = [
582 0xfc, 0xf7, 0xdf, 0xe6, 0x4f, 0xa2, 0xc0, 0x4f, 0x62, 0x35, 0x38, 0x7f, 0x43, 0x4e,
583 0x01, 0x42, 0x23, 0x36, 0xd9, 0xc0, 0x39, 0xde, 0x68, 0x47, 0xa0, 0xb9, 0xdd, 0xcf,
584 0x29, 0xa8, 0x87, 0x59,
585 ];
586
587 let server_hts_key = [
588 0x04, 0x67, 0xf3, 0x16, 0xa8, 0x05, 0xb8, 0xc4, 0x97, 0xee, 0x67, 0x04, 0x7b, 0xbc,
589 0xbc, 0x54,
590 ];
591
592 let server_hts_iv = [
593 0xde, 0x83, 0xa7, 0x3e, 0x9d, 0x81, 0x4b, 0x04, 0xc4, 0x8b, 0x78, 0x09,
594 ];
595
596 let client_ats = [
597 0xc1, 0x4a, 0x6d, 0x79, 0x76, 0xd8, 0x10, 0x2b, 0x5a, 0x0c, 0x99, 0x51, 0x49, 0x3f,
598 0xee, 0x87, 0xdc, 0xaf, 0xf8, 0x2c, 0x24, 0xca, 0xb2, 0x14, 0xe8, 0xbe, 0x71, 0xa8,
599 0x20, 0x6d, 0xbd, 0xa5,
600 ];
601
602 let client_ats_key = [
603 0xcc, 0x9f, 0x5f, 0x98, 0x0b, 0x5f, 0x10, 0x30, 0x6c, 0xba, 0xd7, 0xbe, 0x98, 0xd7,
604 0x57, 0x2e,
605 ];
606
607 let client_ats_iv = [
608 0xb8, 0x09, 0x29, 0xe8, 0xd0, 0x2c, 0x70, 0xf6, 0x11, 0x62, 0xed, 0x6b,
609 ];
610
611 let server_ats = [
612 0x2c, 0x90, 0x77, 0x38, 0xd3, 0xf8, 0x37, 0x02, 0xd1, 0xe4, 0x59, 0x8f, 0x48, 0x48,
613 0x53, 0x1d, 0x9f, 0x93, 0x65, 0x49, 0x1b, 0x9f, 0x7f, 0x52, 0xc8, 0x22, 0x29, 0x0d,
614 0x4c, 0x23, 0x21, 0x92,
615 ];
616
617 let server_ats_key = [
618 0x0c, 0xb2, 0x95, 0x62, 0xd8, 0xd8, 0x8f, 0x48, 0xb0, 0x2c, 0xbf, 0xbe, 0xd7, 0xe6,
619 0x2b, 0xb3,
620 ];
621
622 let server_ats_iv = [
623 0x0d, 0xb2, 0x8f, 0x98, 0x85, 0x86, 0xa1, 0xb7, 0xe4, 0xd5, 0xc6, 0x9c,
624 ];
625
626 let hkdf = hkdf::HKDF_SHA256;
627 let mut ks = KeySchedule::new_with_empty_secret(hkdf);
628 ks.input_secret(&ecdhe_secret);
629
630 assert_traffic_secret(
631 &ks,
632 SecretKind::ClientHandshakeTrafficSecret,
633 &hs_start_hash,
634 &client_hts,
635 &client_hts_key,
636 &client_hts_iv,
637 );
638
639 assert_traffic_secret(
640 &ks,
641 SecretKind::ServerHandshakeTrafficSecret,
642 &hs_start_hash,
643 &server_hts,
644 &server_hts_key,
645 &server_hts_iv,
646 );
647
648 ks.input_empty();
649
650 assert_traffic_secret(
651 &ks,
652 SecretKind::ClientApplicationTrafficSecret,
653 &hs_full_hash,
654 &client_ats,
655 &client_ats_key,
656 &client_ats_iv,
657 );
658
659 assert_traffic_secret(
660 &ks,
661 SecretKind::ServerApplicationTrafficSecret,
662 &hs_full_hash,
663 &server_ats,
664 &server_ats_key,
665 &server_ats_iv,
666 );
667 }
668
669 fn assert_traffic_secret(
670 ks: &KeySchedule,
671 kind: SecretKind,
672 hash: &[u8],
673 expected_traffic_secret: &[u8],
674 expected_key: &[u8],
675 expected_iv: &[u8],
676 ) {
677 struct Log<'a>(&'a [u8]);
678 impl KeyLog for Log<'_> {
679 fn log(&self, _label: &str, _client_random: &[u8], secret: &[u8]) {
680 assert_eq!(self.0, secret);
681 }
682 }
683 let log = Log(expected_traffic_secret);
684 let traffic_secret = ks.derive_logged_secret(kind, &hash, &log, &[0; 32]);
685
686 let aead_alg = &aead::AES_128_GCM;
688 let key = derive_traffic_key(&traffic_secret, aead_alg);
689 let seal_output = seal_zeroes(key);
690 let expected_key = aead::UnboundKey::new(aead_alg, expected_key).unwrap();
691 let expected_seal_output = seal_zeroes(expected_key);
692 assert_eq!(seal_output, expected_seal_output);
693 assert!(seal_output.len() >= 48); let iv = derive_traffic_iv(&traffic_secret);
696 assert_eq!(iv.value(), expected_iv);
697 }
698
699 fn seal_zeroes(key: aead::UnboundKey) -> Vec<u8> {
700 let key = aead::LessSafeKey::new(key);
701 let mut seal_output = vec![0; 32];
702 key.seal_in_place_append_tag(
703 aead::Nonce::assume_unique_for_key([0; aead::NONCE_LEN]),
704 aead::Aad::empty(),
705 &mut seal_output,
706 )
707 .unwrap();
708 seal_output
709 }
710}