Skip to main content

quiche/crypto/
mod.rs

1// Copyright (C) 2018-2019, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use libc::c_int;
28use libc::c_void;
29
30use crate::Error;
31use crate::Result;
32
33use crate::packet;
34
35// All the AEAD algorithms we support use 96-bit nonces.
36pub const MAX_NONCE_LEN: usize = 12;
37
38// Length of header protection mask.
39pub const HP_MASK_LEN: usize = 5;
40
41#[repr(C)]
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum Level {
44    Initial   = 0,
45    ZeroRTT   = 1,
46    Handshake = 2,
47    OneRTT    = 3,
48}
49
50impl Level {
51    pub fn from_epoch(e: packet::Epoch) -> Level {
52        match e {
53            packet::Epoch::Initial => Level::Initial,
54
55            packet::Epoch::Handshake => Level::Handshake,
56
57            packet::Epoch::Application => Level::OneRTT,
58        }
59    }
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub enum Algorithm {
64    #[allow(non_camel_case_types)]
65    AES128_GCM,
66
67    #[allow(non_camel_case_types)]
68    AES256_GCM,
69
70    #[allow(non_camel_case_types)]
71    ChaCha20_Poly1305,
72}
73
74// Note: some vendor-specific methods are implemented in the boringssl
75// submodule.
76impl Algorithm {
77    fn get_evp_digest(self) -> *const EVP_MD {
78        match self {
79            Algorithm::AES128_GCM => unsafe { EVP_sha256() },
80            Algorithm::AES256_GCM => unsafe { EVP_sha384() },
81            Algorithm::ChaCha20_Poly1305 => unsafe { EVP_sha256() },
82        }
83    }
84
85    pub const fn key_len(self) -> usize {
86        match self {
87            Algorithm::AES128_GCM => 16,
88            Algorithm::AES256_GCM => 32,
89            Algorithm::ChaCha20_Poly1305 => 32,
90        }
91    }
92
93    pub const fn tag_len(self) -> usize {
94        if cfg!(feature = "fuzzing") {
95            return 16;
96        }
97
98        match self {
99            Algorithm::AES128_GCM => 16,
100            Algorithm::AES256_GCM => 16,
101            Algorithm::ChaCha20_Poly1305 => 16,
102        }
103    }
104
105    pub const fn nonce_len(self) -> usize {
106        match self {
107            Algorithm::AES128_GCM => 12,
108            Algorithm::AES256_GCM => 12,
109            Algorithm::ChaCha20_Poly1305 => 12,
110        }
111    }
112}
113
114#[allow(non_camel_case_types)]
115#[repr(transparent)]
116pub struct EVP_AEAD {
117    _unused: c_void,
118}
119
120#[allow(non_camel_case_types)]
121#[repr(transparent)]
122struct EVP_MD {
123    _unused: c_void,
124}
125
126type HeaderProtectionMask = [u8; HP_MASK_LEN];
127
128pub struct Open {
129    alg: Algorithm,
130
131    secret: Vec<u8>,
132
133    header: HeaderProtectionKey,
134
135    packet: PacketKey,
136}
137
138impl Open {
139    // Note: some vendor-specific methods are implemented in the boringssl
140    // submodule.
141
142    pub const DECRYPT: u32 = 0;
143
144    pub fn new(
145        alg: Algorithm, key: Vec<u8>, iv: Vec<u8>, hp_key: Vec<u8>,
146        secret: Vec<u8>,
147    ) -> Result<Open> {
148        Ok(Open {
149            alg,
150
151            secret,
152
153            header: HeaderProtectionKey::new(alg, hp_key)?,
154
155            packet: PacketKey::new(alg, key, iv, Self::DECRYPT)?,
156        })
157    }
158
159    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Open> {
160        Ok(Open {
161            alg: aead,
162
163            secret: secret.to_vec(),
164
165            header: HeaderProtectionKey::from_secret(aead, secret)?,
166
167            packet: PacketKey::from_secret(aead, secret, Self::DECRYPT)?,
168        })
169    }
170
171    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
172        if cfg!(feature = "fuzzing") {
173            return Ok(<[u8; 5]>::default());
174        }
175
176        self.header.new_mask(sample)
177    }
178
179    pub fn alg(&self) -> Algorithm {
180        self.alg
181    }
182
183    pub fn derive_next_packet_key(&self) -> Result<Open> {
184        let next_secret = derive_next_secret(self.alg, &self.secret)?;
185
186        let next_packet_key =
187            PacketKey::from_secret(self.alg, &next_secret, Self::DECRYPT)?;
188
189        Ok(Open {
190            alg: self.alg,
191
192            secret: next_secret,
193
194            header: self.header.clone(),
195
196            packet: next_packet_key,
197        })
198    }
199
200    pub fn open_with_u64_counter(
201        &self, counter: u64, ad: &[u8], buf: &mut [u8],
202    ) -> Result<usize> {
203        if cfg!(feature = "fuzzing") {
204            let tag_len = self.alg.tag_len();
205            let out_len = match buf.len().checked_sub(tag_len) {
206                Some(n) => n,
207                None => return Err(Error::CryptoFail),
208            };
209            if ad.len() > tag_len && buf[out_len..] == ad[..tag_len] {
210                return Err(Error::CryptoFail);
211            }
212            return Ok(out_len);
213        }
214
215        self.packet.open_with_u64_counter(counter, ad, buf)
216    }
217}
218
219pub struct Seal {
220    alg: Algorithm,
221
222    secret: Vec<u8>,
223
224    header: HeaderProtectionKey,
225
226    packet: PacketKey,
227}
228
229impl Seal {
230    // Note: some vendor-specific methods are implemented in the boringssl
231    // submodule.
232
233    pub const ENCRYPT: u32 = 1;
234
235    pub fn new(
236        alg: Algorithm, key: Vec<u8>, iv: Vec<u8>, hp_key: Vec<u8>,
237        secret: Vec<u8>,
238    ) -> Result<Seal> {
239        Ok(Seal {
240            alg,
241
242            secret,
243
244            header: HeaderProtectionKey::new(alg, hp_key)?,
245
246            packet: PacketKey::new(alg, key, iv, Self::ENCRYPT)?,
247        })
248    }
249
250    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Seal> {
251        Ok(Seal {
252            alg: aead,
253
254            secret: secret.to_vec(),
255
256            header: HeaderProtectionKey::from_secret(aead, secret)?,
257
258            packet: PacketKey::from_secret(aead, secret, Self::ENCRYPT)?,
259        })
260    }
261
262    pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
263        if cfg!(feature = "fuzzing") {
264            return Ok(<[u8; 5]>::default());
265        }
266
267        self.header.new_mask(sample)
268    }
269
270    pub fn alg(&self) -> Algorithm {
271        self.alg
272    }
273
274    pub fn derive_next_packet_key(&self) -> Result<Seal> {
275        let next_secret = derive_next_secret(self.alg, &self.secret)?;
276
277        let next_packet_key =
278            PacketKey::from_secret(self.alg, &next_secret, Self::ENCRYPT)?;
279
280        Ok(Seal {
281            alg: self.alg,
282
283            secret: next_secret,
284
285            header: self.header.clone(),
286
287            packet: next_packet_key,
288        })
289    }
290
291    pub fn seal_with_u64_counter(
292        &mut self, counter: u64, ad: &[u8], buf: &mut [u8], in_len: usize,
293        extra_in: Option<&[u8]>,
294    ) -> Result<usize> {
295        if cfg!(feature = "fuzzing") {
296            let tag_len = self.alg.tag_len();
297
298            if let Some(extra) = extra_in {
299                if in_len + tag_len + extra.len() > buf.len() {
300                    return Err(Error::CryptoFail);
301                }
302                buf[in_len..in_len + extra.len()].copy_from_slice(extra);
303                return Ok(in_len + extra.len());
304            }
305            if in_len + tag_len > buf.len() {
306                return Err(Error::CryptoFail);
307            }
308
309            return Ok(in_len + tag_len);
310        }
311
312        self.packet
313            .seal_with_u64_counter(counter, ad, buf, in_len, extra_in)
314    }
315}
316
317impl HeaderProtectionKey {
318    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Self> {
319        let key_len = aead.key_len();
320
321        let mut hp_key = vec![0; key_len];
322
323        derive_hdr_key(aead, secret, &mut hp_key)?;
324
325        Self::new(aead, hp_key)
326    }
327}
328
329pub fn derive_initial_key_material(
330    cid: &[u8], version: u32, is_server: bool, did_reset: bool,
331) -> Result<(Open, Seal)> {
332    let mut initial_secret = [0; 32];
333    let mut client_secret = vec![0; 32];
334    let mut server_secret = vec![0; 32];
335
336    let aead = Algorithm::AES128_GCM;
337
338    let key_len = aead.key_len();
339    let nonce_len = aead.nonce_len();
340
341    derive_initial_secret(cid, version, &mut initial_secret)?;
342
343    derive_client_initial_secret(aead, &initial_secret, &mut client_secret)?;
344
345    derive_server_initial_secret(aead, &initial_secret, &mut server_secret)?;
346
347    // When the initial key material has been reset (e.g. due to retry or
348    // version negotiation), we need to prime the AEAD context as well, as the
349    // following packet will not start from 0 again. This is done through the
350    // `Open/Seal::from_secret()` path, rather than `Open/Seal::new()`.
351    if did_reset {
352        let (open, seal) = if is_server {
353            (
354                Open::from_secret(aead, &client_secret)?,
355                Seal::from_secret(aead, &server_secret)?,
356            )
357        } else {
358            (
359                Open::from_secret(aead, &server_secret)?,
360                Seal::from_secret(aead, &client_secret)?,
361            )
362        };
363
364        return Ok((open, seal));
365    }
366
367    // Client.
368    let mut client_key = vec![0; key_len];
369    let mut client_iv = vec![0; nonce_len];
370    let mut client_hp_key = vec![0; key_len];
371
372    derive_pkt_key(aead, &client_secret, &mut client_key)?;
373    derive_pkt_iv(aead, &client_secret, &mut client_iv)?;
374    derive_hdr_key(aead, &client_secret, &mut client_hp_key)?;
375
376    // Server.
377    let mut server_key = vec![0; key_len];
378    let mut server_iv = vec![0; nonce_len];
379    let mut server_hp_key = vec![0; key_len];
380
381    derive_pkt_key(aead, &server_secret, &mut server_key)?;
382    derive_pkt_iv(aead, &server_secret, &mut server_iv)?;
383    derive_hdr_key(aead, &server_secret, &mut server_hp_key)?;
384
385    let (open, seal) = if is_server {
386        (
387            Open::new(aead, client_key, client_iv, client_hp_key, client_secret)?,
388            Seal::new(aead, server_key, server_iv, server_hp_key, server_secret)?,
389        )
390    } else {
391        (
392            Open::new(aead, server_key, server_iv, server_hp_key, server_secret)?,
393            Seal::new(aead, client_key, client_iv, client_hp_key, client_secret)?,
394        )
395    };
396
397    Ok((open, seal))
398}
399
400fn derive_initial_secret(
401    secret: &[u8], version: u32, out_prk: &mut [u8],
402) -> Result<()> {
403    const INITIAL_SALT_V1: [u8; 20] = [
404        0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6,
405        0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
406    ];
407
408    let salt = match version {
409        crate::PROTOCOL_VERSION_V1 => &INITIAL_SALT_V1,
410
411        _ => &INITIAL_SALT_V1,
412    };
413
414    hkdf_extract(Algorithm::AES128_GCM, out_prk, secret, salt)
415}
416
417fn derive_client_initial_secret(
418    aead: Algorithm, prk: &[u8], out: &mut [u8],
419) -> Result<()> {
420    const LABEL: &[u8] = b"client in";
421    hkdf_expand_label(aead, prk, LABEL, out)
422}
423
424fn derive_server_initial_secret(
425    aead: Algorithm, prk: &[u8], out: &mut [u8],
426) -> Result<()> {
427    const LABEL: &[u8] = b"server in";
428    hkdf_expand_label(aead, prk, LABEL, out)
429}
430
431fn derive_next_secret(aead: Algorithm, secret: &[u8]) -> Result<Vec<u8>> {
432    const LABEL: &[u8] = b"quic ku";
433
434    let mut next_secret = vec![0u8; secret.len()];
435
436    hkdf_expand_label(aead, secret, LABEL, &mut next_secret)?;
437
438    Ok(next_secret)
439}
440
441pub fn derive_hdr_key(
442    aead: Algorithm, secret: &[u8], out: &mut [u8],
443) -> Result<()> {
444    const LABEL: &[u8] = b"quic hp";
445
446    let key_len = aead.key_len();
447
448    if key_len > out.len() {
449        return Err(Error::CryptoFail);
450    }
451
452    hkdf_expand_label(aead, secret, LABEL, &mut out[..key_len])
453}
454
455pub fn derive_pkt_key(aead: Algorithm, prk: &[u8], out: &mut [u8]) -> Result<()> {
456    const LABEL: &[u8] = b"quic key";
457
458    let key_len: usize = aead.key_len();
459
460    if key_len > out.len() {
461        return Err(Error::CryptoFail);
462    }
463
464    hkdf_expand_label(aead, prk, LABEL, &mut out[..key_len])
465}
466
467pub fn derive_pkt_iv(aead: Algorithm, prk: &[u8], out: &mut [u8]) -> Result<()> {
468    const LABEL: &[u8] = b"quic iv";
469
470    let nonce_len = aead.nonce_len();
471
472    if nonce_len > out.len() {
473        return Err(Error::CryptoFail);
474    }
475
476    hkdf_expand_label(aead, prk, LABEL, &mut out[..nonce_len])
477}
478
479fn hkdf_expand_label(
480    alg: Algorithm, prk: &[u8], label: &[u8], out: &mut [u8],
481) -> Result<()> {
482    const LABEL_PREFIX: &[u8] = b"tls13 ";
483
484    let out_len = (out.len() as u16).to_be_bytes();
485    let label_len = (LABEL_PREFIX.len() + label.len()) as u8;
486
487    let info = [&out_len, &[label_len][..], LABEL_PREFIX, label, &[0][..]];
488    let info = info.concat();
489
490    hkdf_expand(alg, out, prk, &info)?;
491
492    Ok(())
493}
494
495fn make_nonce(iv: &[u8], counter: u64) -> [u8; MAX_NONCE_LEN] {
496    let mut nonce = [0; MAX_NONCE_LEN];
497    nonce.copy_from_slice(iv);
498
499    // XOR the last bytes of the IV with the counter. This is equivalent to
500    // left-padding the counter with zero bytes.
501    for (a, b) in nonce[4..].iter_mut().zip(counter.to_be_bytes().iter()) {
502        *a ^= b;
503    }
504
505    nonce
506}
507
508pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> {
509    if a.len() != b.len() {
510        return Err(Error::CryptoFail);
511    }
512
513    let rc = unsafe { CRYPTO_memcmp(a.as_ptr(), b.as_ptr(), a.len()) };
514
515    if rc == 0 {
516        return Ok(());
517    }
518
519    Err(Error::CryptoFail)
520}
521
522extern "C" {
523    fn EVP_sha256() -> *const EVP_MD;
524
525    fn EVP_sha384() -> *const EVP_MD;
526
527    // CRYPTO
528    fn CRYPTO_memcmp(a: *const u8, b: *const u8, len: usize) -> c_int;
529}
530
531#[cfg(test)]
532mod tests {
533    use super::*;
534
535    #[test]
536    fn derive_initial_secrets_v1() {
537        let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
538
539        let mut initial_secret = [0; 32];
540
541        let mut secret = [0; 32];
542        let mut pkt_key = [0; 16];
543        let mut pkt_iv = [0; 12];
544        let mut hdr_key = [0; 16];
545
546        let aead = Algorithm::AES128_GCM;
547
548        assert!(derive_initial_secret(
549            &dcid,
550            crate::PROTOCOL_VERSION_V1,
551            &mut initial_secret,
552        )
553        .is_ok());
554
555        // Client.
556        assert!(
557            derive_client_initial_secret(aead, &initial_secret, &mut secret)
558                .is_ok()
559        );
560        let expected_client_initial_secret = [
561            0xc0, 0x0c, 0xf1, 0x51, 0xca, 0x5b, 0xe0, 0x75, 0xed, 0x0e, 0xbf,
562            0xb5, 0xc8, 0x03, 0x23, 0xc4, 0x2d, 0x6b, 0x7d, 0xb6, 0x78, 0x81,
563            0x28, 0x9a, 0xf4, 0x00, 0x8f, 0x1f, 0x6c, 0x35, 0x7a, 0xea,
564        ];
565        assert_eq!(&secret, &expected_client_initial_secret);
566
567        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
568        let expected_client_pkt_key = [
569            0x1f, 0x36, 0x96, 0x13, 0xdd, 0x76, 0xd5, 0x46, 0x77, 0x30, 0xef,
570            0xcb, 0xe3, 0xb1, 0xa2, 0x2d,
571        ];
572        assert_eq!(&pkt_key, &expected_client_pkt_key);
573
574        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
575        let expected_client_pkt_iv = [
576            0xfa, 0x04, 0x4b, 0x2f, 0x42, 0xa3, 0xfd, 0x3b, 0x46, 0xfb, 0x25,
577            0x5c,
578        ];
579        assert_eq!(&pkt_iv, &expected_client_pkt_iv);
580
581        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
582        let expected_client_hdr_key = [
583            0x9f, 0x50, 0x44, 0x9e, 0x04, 0xa0, 0xe8, 0x10, 0x28, 0x3a, 0x1e,
584            0x99, 0x33, 0xad, 0xed, 0xd2,
585        ];
586        assert_eq!(&hdr_key, &expected_client_hdr_key);
587
588        // Server.
589        assert!(
590            derive_server_initial_secret(aead, &initial_secret, &mut secret)
591                .is_ok()
592        );
593
594        let expected_server_initial_secret = [
595            0x3c, 0x19, 0x98, 0x28, 0xfd, 0x13, 0x9e, 0xfd, 0x21, 0x6c, 0x15,
596            0x5a, 0xd8, 0x44, 0xcc, 0x81, 0xfb, 0x82, 0xfa, 0x8d, 0x74, 0x46,
597            0xfa, 0x7d, 0x78, 0xbe, 0x80, 0x3a, 0xcd, 0xda, 0x95, 0x1b,
598        ];
599        assert_eq!(&secret, &expected_server_initial_secret);
600
601        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
602        let expected_server_pkt_key = [
603            0xcf, 0x3a, 0x53, 0x31, 0x65, 0x3c, 0x36, 0x4c, 0x88, 0xf0, 0xf3,
604            0x79, 0xb6, 0x06, 0x7e, 0x37,
605        ];
606        assert_eq!(&pkt_key, &expected_server_pkt_key);
607
608        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
609        let expected_server_pkt_iv = [
610            0x0a, 0xc1, 0x49, 0x3c, 0xa1, 0x90, 0x58, 0x53, 0xb0, 0xbb, 0xa0,
611            0x3e,
612        ];
613        assert_eq!(&pkt_iv, &expected_server_pkt_iv);
614
615        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
616        let expected_server_hdr_key = [
617            0xc2, 0x06, 0xb8, 0xd9, 0xb9, 0xf0, 0xf3, 0x76, 0x44, 0x43, 0x0b,
618            0x49, 0x0e, 0xea, 0xa3, 0x14,
619        ];
620        assert_eq!(&hdr_key, &expected_server_hdr_key);
621    }
622
623    #[test]
624    fn derive_chacha20_secrets() {
625        let secret = [
626            0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42,
627            0x27, 0x48, 0xad, 0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0,
628            0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3, 0x0f, 0x21, 0x63, 0x2b,
629        ];
630
631        let aead = Algorithm::ChaCha20_Poly1305;
632
633        let mut pkt_key = [0; 32];
634        let mut pkt_iv = [0; 12];
635        let mut hdr_key = [0; 32];
636
637        assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok());
638        let expected_pkt_key = [
639            0xc6, 0xd9, 0x8f, 0xf3, 0x44, 0x1c, 0x3f, 0xe1, 0xb2, 0x18, 0x20,
640            0x94, 0xf6, 0x9c, 0xaa, 0x2e, 0xd4, 0xb7, 0x16, 0xb6, 0x54, 0x88,
641            0x96, 0x0a, 0x7a, 0x98, 0x49, 0x79, 0xfb, 0x23, 0xe1, 0xc8,
642        ];
643        assert_eq!(&pkt_key, &expected_pkt_key);
644
645        assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok());
646        let expected_pkt_iv = [
647            0xe0, 0x45, 0x9b, 0x34, 0x74, 0xbd, 0xd0, 0xe4, 0x4a, 0x41, 0xc1,
648            0x44,
649        ];
650        assert_eq!(&pkt_iv, &expected_pkt_iv);
651
652        assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok());
653        let expected_hdr_key = [
654            0x25, 0xa2, 0x82, 0xb9, 0xe8, 0x2f, 0x06, 0xf2, 0x1f, 0x48, 0x89,
655            0x17, 0xa4, 0xfc, 0x8f, 0x1b, 0x73, 0x57, 0x36, 0x85, 0x60, 0x85,
656            0x97, 0xd0, 0xef, 0xcb, 0x07, 0x6b, 0x0a, 0xb7, 0xa7, 0xa4,
657        ];
658        assert_eq!(&hdr_key, &expected_hdr_key);
659
660        let next_secret = derive_next_secret(aead, &secret).unwrap();
661        let expected_secret = [
662            0x12, 0x23, 0x50, 0x47, 0x55, 0x03, 0x6d, 0x55, 0x63, 0x42, 0xee,
663            0x93, 0x61, 0xd2, 0x53, 0x42, 0x1a, 0x82, 0x6c, 0x9e, 0xcd, 0xf3,
664            0xc7, 0x14, 0x86, 0x84, 0xb3, 0x6b, 0x71, 0x48, 0x81, 0xf9,
665        ];
666        assert_eq!(&next_secret, &expected_secret);
667    }
668}
669
670mod boringssl;
671pub(crate) use boringssl::*;