1 /// This module contains optional APIs for implementing QUIC TLS.
2 use crate::client::{ClientConfig, ClientSession, ClientSessionImpl};
3 use crate::error::TLSError;
4 use crate::key_schedule::hkdf_expand;
5 use crate::msgs::enums::{AlertDescription, ContentType, ProtocolVersion};
6 use crate::msgs::handshake::{ClientExtension, ServerExtension};
7 use crate::msgs::message::{Message, MessagePayload};
8 use crate::server::{ServerConfig, ServerSession, ServerSessionImpl};
9 use crate::session::{Protocol, SessionCommon};
10 use crate::suites::{BulkAlgorithm, SupportedCipherSuite, TLS13_AES_128_GCM_SHA256};
11 
12 use std::sync::Arc;
13 
14 use ring::{aead, hkdf};
15 use webpki;
16 
17 /// Secrets used to encrypt/decrypt traffic
18 #[derive(Clone, Debug)]
19 pub(crate) struct Secrets {
20     /// Secret used to encrypt packets transmitted by the client
21     pub client: hkdf::Prk,
22     /// Secret used to encrypt packets transmitted by the server
23     pub server: hkdf::Prk,
24 }
25 
26 impl Secrets {
local_remote(&self, is_client: bool) -> (&hkdf::Prk, &hkdf::Prk)27     fn local_remote(&self, is_client: bool) -> (&hkdf::Prk, &hkdf::Prk) {
28         if is_client {
29             (&self.client, &self.server)
30         } else {
31             (&self.server, &self.client)
32         }
33     }
34 }
35 
36 /// Generic methods for QUIC sessions
37 pub trait QuicExt {
38     /// Return the TLS-encoded transport parameters for the session's peer.
get_quic_transport_parameters(&self) -> Option<&[u8]>39     fn get_quic_transport_parameters(&self) -> Option<&[u8]>;
40 
41     /// Compute the keys for encrypting/decrypting 0-RTT packets, if available
get_0rtt_keys(&self) -> Option<DirectionalKeys>42     fn get_0rtt_keys(&self) -> Option<DirectionalKeys>;
43 
44     /// Consume unencrypted TLS handshake data.
45     ///
46     /// Handshake data obtained from separate encryption levels should be supplied in separate calls.
read_hs(&mut self, plaintext: &[u8]) -> Result<(), TLSError>47     fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), TLSError>;
48 
49     /// Emit unencrypted TLS handshake data.
50     ///
51     /// When this returns `Some(_)`, the new keys must be used for future handshake data.
write_hs(&mut self, buf: &mut Vec<u8>) -> Option<Keys>52     fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
53 
54     /// Emit the TLS description code of a fatal alert, if one has arisen.
55     ///
56     /// Check after `read_hs` returns `Err(_)`.
get_alert(&self) -> Option<AlertDescription>57     fn get_alert(&self) -> Option<AlertDescription>;
58 
59     /// Compute the keys to use following a 1-RTT key update
60     ///
61     /// Must not be called until the handshake is complete
next_1rtt_keys(&mut self) -> PacketKeySet62     fn next_1rtt_keys(&mut self) -> PacketKeySet;
63 }
64 
65 impl QuicExt for ClientSession {
get_quic_transport_parameters(&self) -> Option<&[u8]>66     fn get_quic_transport_parameters(&self) -> Option<&[u8]> {
67         self.imp
68             .common
69             .quic
70             .params
71             .as_ref()
72             .map(|v| v.as_ref())
73     }
74 
get_0rtt_keys(&self) -> Option<DirectionalKeys>75     fn get_0rtt_keys(&self) -> Option<DirectionalKeys> {
76         Some(DirectionalKeys::new(
77             self.imp.resumption_ciphersuite?,
78             self.imp
79                 .common
80                 .quic
81                 .early_secret
82                 .as_ref()?,
83         ))
84     }
85 
read_hs(&mut self, plaintext: &[u8]) -> Result<(), TLSError>86     fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), TLSError> {
87         read_hs(&mut self.imp.common, plaintext)?;
88         self.imp
89             .process_new_handshake_messages()
90     }
91 
write_hs(&mut self, buf: &mut Vec<u8>) -> Option<Keys>92     fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
93         write_hs(&mut self.imp.common, buf)
94     }
95 
get_alert(&self) -> Option<AlertDescription>96     fn get_alert(&self) -> Option<AlertDescription> {
97         self.imp.common.quic.alert
98     }
99 
next_1rtt_keys(&mut self) -> PacketKeySet100     fn next_1rtt_keys(&mut self) -> PacketKeySet {
101         next_1rtt_keys(&mut self.imp.common)
102     }
103 }
104 
105 impl QuicExt for ServerSession {
get_quic_transport_parameters(&self) -> Option<&[u8]>106     fn get_quic_transport_parameters(&self) -> Option<&[u8]> {
107         self.imp
108             .common
109             .quic
110             .params
111             .as_ref()
112             .map(|v| v.as_ref())
113     }
114 
get_0rtt_keys(&self) -> Option<DirectionalKeys>115     fn get_0rtt_keys(&self) -> Option<DirectionalKeys> {
116         Some(DirectionalKeys::new(
117             self.imp.common.get_suite()?,
118             self.imp
119                 .common
120                 .quic
121                 .early_secret
122                 .as_ref()?,
123         ))
124     }
125 
read_hs(&mut self, plaintext: &[u8]) -> Result<(), TLSError>126     fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), TLSError> {
127         read_hs(&mut self.imp.common, plaintext)?;
128         self.imp
129             .process_new_handshake_messages()
130     }
write_hs(&mut self, buf: &mut Vec<u8>) -> Option<Keys>131     fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
132         write_hs(&mut self.imp.common, buf)
133     }
134 
get_alert(&self) -> Option<AlertDescription>135     fn get_alert(&self) -> Option<AlertDescription> {
136         self.imp.common.quic.alert
137     }
138 
next_1rtt_keys(&mut self) -> PacketKeySet139     fn next_1rtt_keys(&mut self) -> PacketKeySet {
140         next_1rtt_keys(&mut self.imp.common)
141     }
142 }
143 
144 /// Keys used to communicate in a single direction
145 pub struct DirectionalKeys {
146     /// Encrypts or decrypts a packet's headers
147     pub header: aead::quic::HeaderProtectionKey,
148     /// Encrypts or decrypts the payload of a packet
149     pub packet: PacketKey,
150 }
151 
152 impl DirectionalKeys {
new(suite: &'static SupportedCipherSuite, secret: &hkdf::Prk) -> Self153     fn new(suite: &'static SupportedCipherSuite, secret: &hkdf::Prk) -> Self {
154         let hp_alg = match suite.bulk {
155             BulkAlgorithm::AES_128_GCM => &aead::quic::AES_128,
156             BulkAlgorithm::AES_256_GCM => &aead::quic::AES_256,
157             BulkAlgorithm::CHACHA20_POLY1305 => &aead::quic::CHACHA20,
158         };
159 
160         Self {
161             header: hkdf_expand(secret, hp_alg, b"quic hp", &[]),
162             packet: PacketKey::new(suite, secret),
163         }
164     }
165 }
166 
167 /// Keys to encrypt or decrypt the payload of a packet
168 pub struct PacketKey {
169     /// Encrypts or decrypts a packet's payload
170     pub key: aead::LessSafeKey,
171     /// Computes unique nonces for each packet
172     pub iv: Iv,
173 }
174 
175 impl PacketKey {
new(suite: &'static SupportedCipherSuite, secret: &hkdf::Prk) -> Self176     fn new(suite: &'static SupportedCipherSuite, secret: &hkdf::Prk) -> Self {
177         Self {
178             key: aead::LessSafeKey::new(hkdf_expand(
179                 secret,
180                 suite.aead_algorithm,
181                 b"quic key",
182                 &[],
183             )),
184             iv: hkdf_expand(secret, IvLen, b"quic iv", &[]),
185         }
186     }
187 }
188 
189 /// Packet protection keys for bidirectional 1-RTT communication
190 pub struct PacketKeySet {
191     /// Encrypts outgoing packets
192     pub local: PacketKey,
193     /// Decrypts incoming packets
194     pub remote: PacketKey,
195 }
196 
197 /// Computes unique nonces for each packet
198 pub struct Iv([u8; aead::NONCE_LEN]);
199 
200 impl Iv {
201     /// Compute the nonce to use for encrypting or decrypting `packet_number`
nonce_for(&self, packet_number: u64) -> ring::aead::Nonce202     pub fn nonce_for(&self, packet_number: u64) -> ring::aead::Nonce {
203         let mut out = [0; aead::NONCE_LEN];
204         out[4..].copy_from_slice(&packet_number.to_be_bytes());
205         for (out, inp) in out.iter_mut().zip(self.0.iter()) {
206             *out ^= inp;
207         }
208         aead::Nonce::assume_unique_for_key(out)
209     }
210 }
211 
212 impl From<hkdf::Okm<'_, IvLen>> for Iv {
from(okm: hkdf::Okm<IvLen>) -> Self213     fn from(okm: hkdf::Okm<IvLen>) -> Self {
214         let mut iv = [0; aead::NONCE_LEN];
215         okm.fill(&mut iv[..]).unwrap();
216         Iv(iv)
217     }
218 }
219 
220 struct IvLen;
221 
222 impl hkdf::KeyType for IvLen {
len(&self) -> usize223     fn len(&self) -> usize {
224         aead::NONCE_LEN
225     }
226 }
227 
228 /// Complete set of keys used to communicate with the peer
229 pub struct Keys {
230     /// Encrypts outgoing packets
231     pub local: DirectionalKeys,
232     /// Decrypts incoming packets
233     pub remote: DirectionalKeys,
234 }
235 
236 impl Keys {
237     /// Construct keys for use with initial packets
initial( initial_salt: &hkdf::Salt, client_dst_connection_id: &[u8], is_client: bool, ) -> Self238     pub fn initial(
239         initial_salt: &hkdf::Salt,
240         client_dst_connection_id: &[u8],
241         is_client: bool,
242     ) -> Self {
243         const CLIENT_LABEL: &[u8] = b"client in";
244         const SERVER_LABEL: &[u8] = b"server in";
245         let hs_secret = initial_salt.extract(client_dst_connection_id);
246 
247         let secrets = Secrets {
248             client: hkdf_expand(&hs_secret, hkdf::HKDF_SHA256, CLIENT_LABEL, &[]),
249             server: hkdf_expand(&hs_secret, hkdf::HKDF_SHA256, SERVER_LABEL, &[]),
250         };
251         Self::new(&TLS13_AES_128_GCM_SHA256, is_client, &secrets)
252     }
253 
new(suite: &'static SupportedCipherSuite, is_client: bool, secrets: &Secrets) -> Self254     fn new(suite: &'static SupportedCipherSuite, is_client: bool, secrets: &Secrets) -> Self {
255         let (local, remote) = secrets.local_remote(is_client);
256         Keys {
257             local: DirectionalKeys::new(suite, local),
258             remote: DirectionalKeys::new(suite, remote),
259         }
260     }
261 }
262 
read_hs(this: &mut SessionCommon, plaintext: &[u8]) -> Result<(), TLSError>263 fn read_hs(this: &mut SessionCommon, plaintext: &[u8]) -> Result<(), TLSError> {
264     if this
265         .handshake_joiner
266         .take_message(Message {
267             typ: ContentType::Handshake,
268             version: ProtocolVersion::TLSv1_3,
269             payload: MessagePayload::new_opaque(plaintext.into()),
270         })
271         .is_none()
272     {
273         this.quic.alert = Some(AlertDescription::DecodeError);
274         return Err(TLSError::CorruptMessage);
275     }
276     Ok(())
277 }
278 
write_hs(this: &mut SessionCommon, buf: &mut Vec<u8>) -> Option<Keys>279 fn write_hs(this: &mut SessionCommon, buf: &mut Vec<u8>) -> Option<Keys> {
280     while let Some((_, msg)) = this.quic.hs_queue.pop_front() {
281         buf.extend_from_slice(&msg);
282         if let Some(&(true, _)) = this.quic.hs_queue.front() {
283             if this.quic.hs_secrets.is_some() {
284                 // Allow the caller to switch keys before proceeding.
285                 break;
286             }
287         }
288     }
289     if let Some(secrets) = this.quic.hs_secrets.take() {
290         return Some(Keys::new(this.get_suite_assert(), this.is_client, &secrets));
291     }
292     if let Some(secrets) = this.quic.traffic_secrets.as_ref() {
293         if !this.quic.returned_traffic_keys {
294             this.quic.returned_traffic_keys = true;
295             return Some(Keys::new(this.get_suite_assert(), this.is_client, &secrets));
296         }
297     }
298     None
299 }
300 
next_1rtt_keys(this: &mut SessionCommon) -> PacketKeySet301 fn next_1rtt_keys(this: &mut SessionCommon) -> PacketKeySet {
302     let hkdf_alg = this.get_suite_assert().hkdf_algorithm;
303     let secrets = this
304         .quic
305         .traffic_secrets
306         .as_ref()
307         .expect("traffic keys not yet available");
308 
309     let next = next_1rtt_secrets(hkdf_alg, secrets);
310 
311     let (local, remote) = next.local_remote(this.is_client);
312     let keys = PacketKeySet {
313         local: PacketKey::new(this.get_suite_assert(), local),
314         remote: PacketKey::new(this.get_suite_assert(), remote),
315     };
316 
317     this.quic.traffic_secrets = Some(next);
318     keys
319 }
320 
next_1rtt_secrets(hkdf_alg: hkdf::Algorithm, prev: &Secrets) -> Secrets321 fn next_1rtt_secrets(hkdf_alg: hkdf::Algorithm, prev: &Secrets) -> Secrets {
322     Secrets {
323         client: hkdf_expand(&prev.client, hkdf_alg, b"quic ku", &[]),
324         server: hkdf_expand(&prev.server, hkdf_alg, b"quic ku", &[]),
325     }
326 }
327 
328 /// Methods specific to QUIC client sessions
329 pub trait ClientQuicExt {
330     /// Make a new QUIC ClientSession. This differs from `ClientSession::new()`
331     /// in that it takes an extra argument, `params`, which contains the
332     /// TLS-encoded transport parameters to send.
new_quic( config: &Arc<ClientConfig>, hostname: webpki::DNSNameRef, params: Vec<u8>, ) -> ClientSession333     fn new_quic(
334         config: &Arc<ClientConfig>,
335         hostname: webpki::DNSNameRef,
336         params: Vec<u8>,
337     ) -> ClientSession {
338         assert!(
339             config
340                 .versions
341                 .iter()
342                 .all(|x| x.get_u16() >= ProtocolVersion::TLSv1_3.get_u16()),
343             "QUIC requires TLS version >= 1.3"
344         );
345         let mut imp = ClientSessionImpl::new(config);
346         imp.common.protocol = Protocol::Quic;
347         imp.start_handshake(
348             hostname.into(),
349             vec![ClientExtension::TransportParameters(params)],
350         );
351         ClientSession { imp }
352     }
353 }
354 
355 impl ClientQuicExt for ClientSession {}
356 
357 /// Methods specific to QUIC server sessions
358 pub trait ServerQuicExt {
359     /// Make a new QUIC ServerSession. This differs from `ServerSession::new()`
360     /// in that it takes an extra argument, `params`, which contains the
361     /// TLS-encoded transport parameters to send.
new_quic(config: &Arc<ServerConfig>, params: Vec<u8>) -> ServerSession362     fn new_quic(config: &Arc<ServerConfig>, params: Vec<u8>) -> ServerSession {
363         assert!(
364             config
365                 .versions
366                 .iter()
367                 .all(|x| x.get_u16() >= ProtocolVersion::TLSv1_3.get_u16()),
368             "QUIC requires TLS version >= 1.3"
369         );
370         assert!(
371             config.max_early_data_size == 0 || config.max_early_data_size == 0xffff_ffff,
372             "QUIC sessions must set a max early data of 0 or 2^32-1"
373         );
374         let mut imp =
375             ServerSessionImpl::new(config, vec![ServerExtension::TransportParameters(params)]);
376         imp.common.protocol = Protocol::Quic;
377         ServerSession { imp }
378     }
379 }
380 
381 impl ServerQuicExt for ServerSession {}
382 
383 #[cfg(test)]
384 mod test {
385     use super::*;
386 
387     #[test]
initial_keys_test_vectors()388     fn initial_keys_test_vectors() {
389         // Test vectors based on draft 27
390         const INITIAL_SALT: [u8; 20] = [
391             0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4,
392             0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
393         ];
394 
395         const CONNECTION_ID: &[u8] = &[0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
396         const PACKET_NUMBER: u64 = 42;
397 
398         let initial_salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &INITIAL_SALT);
399         let server_keys = Keys::initial(&initial_salt, &CONNECTION_ID, false);
400         let client_keys = Keys::initial(&initial_salt, &CONNECTION_ID, true);
401 
402         // Nonces
403         const SERVER_NONCE: [u8; 12] = [
404             0x5e, 0x5a, 0xe6, 0x51, 0xfd, 0x1e, 0x84, 0x95, 0xaf, 0x13, 0x50, 0xa1,
405         ];
406         assert_eq!(
407             server_keys
408                 .local
409                 .packet
410                 .iv
411                 .nonce_for(PACKET_NUMBER)
412                 .as_ref(),
413             &SERVER_NONCE
414         );
415         assert_eq!(
416             client_keys
417                 .remote
418                 .packet
419                 .iv
420                 .nonce_for(PACKET_NUMBER)
421                 .as_ref(),
422             &SERVER_NONCE
423         );
424         const CLIENT_NONCE: [u8; 12] = [
425             0x86, 0x81, 0x35, 0x94, 0x10, 0xa7, 0x0b, 0xb9, 0xc9, 0x2f, 0x04, 0x0a,
426         ];
427         assert_eq!(
428             server_keys
429                 .remote
430                 .packet
431                 .iv
432                 .nonce_for(PACKET_NUMBER)
433                 .as_ref(),
434             &CLIENT_NONCE
435         );
436         assert_eq!(
437             client_keys
438                 .local
439                 .packet
440                 .iv
441                 .nonce_for(PACKET_NUMBER)
442                 .as_ref(),
443             &CLIENT_NONCE
444         );
445 
446         // Header encryption mask
447         const SAMPLE: &[u8] = &[
448             0x70, 0x02, 0x59, 0x6f, 0x99, 0xae, 0x67, 0xab, 0xf6, 0x5a, 0x58, 0x52, 0xf5, 0x4f,
449             0x58, 0xc3,
450         ];
451 
452         const SERVER_MASK: [u8; 5] = [0x38, 0x16, 0x8a, 0x0c, 0x25];
453         assert_eq!(
454             server_keys
455                 .local
456                 .header
457                 .new_mask(SAMPLE)
458                 .unwrap(),
459             SERVER_MASK
460         );
461         assert_eq!(
462             client_keys
463                 .remote
464                 .header
465                 .new_mask(SAMPLE)
466                 .unwrap(),
467             SERVER_MASK
468         );
469         const CLIENT_MASK: [u8; 5] = [0xae, 0x96, 0x2e, 0x67, 0xec];
470         assert_eq!(
471             server_keys
472                 .remote
473                 .header
474                 .new_mask(SAMPLE)
475                 .unwrap(),
476             CLIENT_MASK
477         );
478         assert_eq!(
479             client_keys
480                 .local
481                 .header
482                 .new_mask(SAMPLE)
483                 .unwrap(),
484             CLIENT_MASK
485         );
486 
487         const AAD: &[u8] = &[
488             0xc9, 0xff, 0x00, 0x00, 0x1b, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62,
489             0xb5, 0x00, 0x40, 0x74, 0x16, 0x8b,
490         ];
491         let aad = aead::Aad::from(AAD);
492         const PLAINTEXT: [u8; 12] = [
493             0x0d, 0x00, 0x00, 0x00, 0x00, 0x18, 0x41, 0x0a, 0x02, 0x00, 0x00, 0x56,
494         ];
495         let mut payload = PLAINTEXT;
496         let server_nonce = server_keys
497             .local
498             .packet
499             .iv
500             .nonce_for(PACKET_NUMBER);
501         let tag = server_keys
502             .local
503             .packet
504             .key
505             .seal_in_place_separate_tag(server_nonce, aad, &mut payload)
506             .unwrap();
507         assert_eq!(
508             payload,
509             [
510                 0x0d, 0x91, 0x96, 0x31, 0xc0, 0xeb, 0x84, 0xf2, 0x88, 0x59, 0xfe, 0xc0
511             ]
512         );
513         assert_eq!(
514             tag.as_ref(),
515             &[
516                 0xdf, 0xee, 0x06, 0x81, 0x9e, 0x7a, 0x08, 0x34, 0xe4, 0x94, 0x19, 0x79, 0x5f, 0xe0,
517                 0xd7, 0x3f
518             ]
519         );
520 
521         let aad = aead::Aad::from(AAD);
522         let mut payload = PLAINTEXT;
523         let client_nonce = client_keys
524             .local
525             .packet
526             .iv
527             .nonce_for(PACKET_NUMBER);
528         let tag = client_keys
529             .local
530             .packet
531             .key
532             .seal_in_place_separate_tag(client_nonce, aad, &mut payload)
533             .unwrap();
534         assert_eq!(
535             payload,
536             [
537                 0x89, 0x6c, 0x66, 0x91, 0xe0, 0x9f, 0x47, 0x7a, 0x91, 0x42, 0xa4, 0x46
538             ]
539         );
540         assert_eq!(
541             tag.as_ref(),
542             &[
543                 0xb6, 0xff, 0xef, 0x89, 0xd5, 0xcb, 0x53, 0xd0, 0x98, 0xf7, 0x40, 0xa, 0x8d, 0x97,
544                 0x72, 0x6e
545             ]
546         );
547     }
548 
549     #[test]
key_update_test_vector()550     fn key_update_test_vector() {
551         fn equal_prk(x: &hkdf::Prk, y: &hkdf::Prk) -> bool {
552             let mut x_data = [0; 16];
553             let mut y_data = [0; 16];
554             let x_okm = x
555                 .expand(&[b"info"], &aead::quic::AES_128)
556                 .unwrap();
557             x_okm.fill(&mut x_data[..]).unwrap();
558             let y_okm = y
559                 .expand(&[b"info"], &aead::quic::AES_128)
560                 .unwrap();
561             y_okm.fill(&mut y_data[..]).unwrap();
562             x_data == y_data
563         }
564 
565         let initial = Secrets {
566             // Constant dummy values for reproducibility
567             client: hkdf::Prk::new_less_safe(
568                 hkdf::HKDF_SHA256,
569                 &[
570                     0xb8, 0x76, 0x77, 0x08, 0xf8, 0x77, 0x23, 0x58, 0xa6, 0xea, 0x9f, 0xc4, 0x3e,
571                     0x4a, 0xdd, 0x2c, 0x96, 0x1b, 0x3f, 0x52, 0x87, 0xa6, 0xd1, 0x46, 0x7e, 0xe0,
572                     0xae, 0xab, 0x33, 0x72, 0x4d, 0xbf,
573                 ],
574             ),
575             server: hkdf::Prk::new_less_safe(
576                 hkdf::HKDF_SHA256,
577                 &[
578                     0x42, 0xdc, 0x97, 0x21, 0x40, 0xe0, 0xf2, 0xe3, 0x98, 0x45, 0xb7, 0x67, 0x61,
579                     0x34, 0x39, 0xdc, 0x67, 0x58, 0xca, 0x43, 0x25, 0x9b, 0x87, 0x85, 0x06, 0x82,
580                     0x4e, 0xb1, 0xe4, 0x38, 0xd8, 0x55,
581                 ],
582             ),
583         };
584         let updated = next_1rtt_secrets(hkdf::HKDF_SHA256, &initial);
585 
586         assert!(equal_prk(
587             &updated.client,
588             &hkdf::Prk::new_less_safe(
589                 hkdf::HKDF_SHA256,
590                 &[
591                     0x42, 0xca, 0xc8, 0xc9, 0x1c, 0xd5, 0xeb, 0x40, 0x68, 0x2e, 0x43, 0x2e, 0xdf,
592                     0x2d, 0x2b, 0xe9, 0xf4, 0x1a, 0x52, 0xca, 0x6b, 0x22, 0xd8, 0xe6, 0xcd, 0xb1,
593                     0xe8, 0xac, 0xa9, 0x6, 0x1f, 0xce
594                 ]
595             )
596         ));
597         assert!(equal_prk(
598             &updated.server,
599             &hkdf::Prk::new_less_safe(
600                 hkdf::HKDF_SHA256,
601                 &[
602                     0xeb, 0x7f, 0x5e, 0x2a, 0x12, 0x3f, 0x40, 0x7d, 0xb4, 0x99, 0xe3, 0x61, 0xca,
603                     0xe5, 0x90, 0xd4, 0xd9, 0x92, 0xe1, 0x4b, 0x7a, 0xce, 0x3, 0xc2, 0x44, 0xe0,
604                     0x42, 0x21, 0x15, 0xb6, 0xd3, 0x8a
605                 ]
606             )
607         ));
608     }
609 }
610