rustls/crypto/aws_lc_rs/pq/
hybrid.rs1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use super::INVALID_KEY_SHARE;
5use crate::crypto::{ActiveKeyExchange, CompletedKeyExchange, SharedSecret, SupportedKxGroup};
6use crate::ffdhe_groups::FfdheGroup;
7use crate::{Error, NamedGroup, ProtocolVersion};
8
9#[derive(Debug)]
11pub(crate) struct Hybrid {
12 pub(crate) classical: &'static dyn SupportedKxGroup,
13 pub(crate) post_quantum: &'static dyn SupportedKxGroup,
14 pub(crate) name: NamedGroup,
15 pub(crate) layout: Layout,
16}
17
18impl SupportedKxGroup for Hybrid {
19 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
20 let classical = self.classical.start()?;
21 let post_quantum = self.post_quantum.start()?;
22
23 let combined_pub_key = self
24 .layout
25 .concat(post_quantum.pub_key(), classical.pub_key());
26
27 Ok(Box::new(ActiveHybrid {
28 classical,
29 post_quantum,
30 name: self.name,
31 layout: self.layout,
32 combined_pub_key,
33 }))
34 }
35
36 fn start_and_complete(&self, client_share: &[u8]) -> Result<CompletedKeyExchange, Error> {
37 let (post_quantum_share, classical_share) = self
38 .layout
39 .split_received_client_share(client_share)
40 .ok_or(INVALID_KEY_SHARE)?;
41
42 let cl = self
43 .classical
44 .start_and_complete(classical_share)?;
45 let pq = self
46 .post_quantum
47 .start_and_complete(post_quantum_share)?;
48
49 let combined_pub_key = self
50 .layout
51 .concat(&pq.pub_key, &cl.pub_key);
52 let secret = self
53 .layout
54 .concat(pq.secret.secret_bytes(), cl.secret.secret_bytes());
55
56 Ok(CompletedKeyExchange {
57 group: self.name,
58 pub_key: combined_pub_key,
59 secret: SharedSecret::from(secret),
60 })
61 }
62
63 fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
64 None
65 }
66
67 fn name(&self) -> NamedGroup {
68 self.name
69 }
70
71 fn fips(&self) -> bool {
72 match self.layout.post_quantum_first {
87 true => self.post_quantum.fips(),
88 false => self.classical.fips(),
89 }
90 }
91
92 fn usable_for_version(&self, version: ProtocolVersion) -> bool {
93 version == ProtocolVersion::TLSv1_3
94 }
95}
96
97struct ActiveHybrid {
98 classical: Box<dyn ActiveKeyExchange>,
99 post_quantum: Box<dyn ActiveKeyExchange>,
100 name: NamedGroup,
101 layout: Layout,
102 combined_pub_key: Vec<u8>,
103}
104
105impl ActiveKeyExchange for ActiveHybrid {
106 fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
107 let (post_quantum_share, classical_share) = self
108 .layout
109 .split_received_server_share(peer_pub_key)
110 .ok_or(INVALID_KEY_SHARE)?;
111
112 let cl = self
113 .classical
114 .complete(classical_share)?;
115 let pq = self
116 .post_quantum
117 .complete(post_quantum_share)?;
118
119 let secret = self
120 .layout
121 .concat(pq.secret_bytes(), cl.secret_bytes());
122 Ok(SharedSecret::from(secret))
123 }
124
125 fn hybrid_component(&self) -> Option<(NamedGroup, &[u8])> {
127 Some((self.classical.group(), self.classical.pub_key()))
128 }
129
130 fn complete_hybrid_component(
131 self: Box<Self>,
132 peer_pub_key: &[u8],
133 ) -> Result<SharedSecret, Error> {
134 self.classical.complete(peer_pub_key)
135 }
136
137 fn pub_key(&self) -> &[u8] {
138 &self.combined_pub_key
139 }
140
141 fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
142 None
143 }
144
145 fn group(&self) -> NamedGroup {
146 self.name
147 }
148}
149
150#[derive(Clone, Copy, Debug)]
151pub(crate) struct Layout {
152 pub(crate) classical_share_len: usize,
154
155 pub(crate) post_quantum_client_share_len: usize,
157
158 pub(crate) post_quantum_server_share_len: usize,
160
161 pub(crate) post_quantum_first: bool,
166}
167
168impl Layout {
169 fn split_received_client_share<'a>(&self, share: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
170 self.split(share, self.post_quantum_client_share_len)
171 }
172
173 fn split_received_server_share<'a>(&self, share: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
174 self.split(share, self.post_quantum_server_share_len)
175 }
176
177 fn split<'a>(
179 &self,
180 share: &'a [u8],
181 post_quantum_share_len: usize,
182 ) -> Option<(&'a [u8], &'a [u8])> {
183 if share.len() != self.classical_share_len + post_quantum_share_len {
184 return None;
185 }
186
187 Some(match self.post_quantum_first {
188 true => {
189 let (first_share, second_share) = share.split_at(post_quantum_share_len);
190 (first_share, second_share)
191 }
192 false => {
193 let (first_share, second_share) = share.split_at(self.classical_share_len);
194 (second_share, first_share)
195 }
196 })
197 }
198
199 fn concat(&self, post_quantum: &[u8], classical: &[u8]) -> Vec<u8> {
200 match self.post_quantum_first {
201 true => [post_quantum, classical].concat(),
202 false => [classical, post_quantum].concat(),
203 }
204 }
205}