1use crate::{
2 err::{Error, Res},
3 hpke::{Aead as AeadId, Kdf, Kem},
4 KeyId,
5};
6use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
7use std::{
8 convert::TryFrom,
9 io::{BufRead, BufReader, Cursor, Read},
10};
11
12#[cfg(feature = "nss")]
13use crate::nss::{
14 hpke::{generate_key_pair, Config as HpkeConfig, HpkeR},
15 PrivateKey, PublicKey,
16};
17
18#[cfg(feature = "rust-hpke")]
19use crate::rh::hpke::{
20 derive_key_pair, generate_key_pair, Config as HpkeConfig, HpkeR, PrivateKey, PublicKey,
21};
22
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25pub struct SymmetricSuite {
26 kdf: Kdf,
27 aead: AeadId,
28}
29
30impl SymmetricSuite {
31 #[must_use]
32 pub const fn new(kdf: Kdf, aead: AeadId) -> Self {
33 Self { kdf, aead }
34 }
35
36 #[must_use]
37 pub fn kdf(self) -> Kdf {
38 self.kdf
39 }
40
41 #[must_use]
42 pub fn aead(self) -> AeadId {
43 self.aead
44 }
45}
46
47#[allow(clippy::module_name_repetitions)]
51#[derive(Debug, Clone)]
52pub struct KeyConfig {
53 pub(crate) key_id: KeyId,
54 pub(crate) kem: Kem,
55 pub(crate) symmetric: Vec<SymmetricSuite>,
56 pub(crate) sk: Option<PrivateKey>,
57 pub(crate) pk: PublicKey,
58}
59
60impl KeyConfig {
61 fn strip_unsupported(symmetric: &mut Vec<SymmetricSuite>, kem: Kem) {
62 symmetric.retain(|s| HpkeConfig::new(kem, s.kdf(), s.aead()).supported());
63 }
64
65 pub fn new(key_id: u8, kem: Kem, mut symmetric: Vec<SymmetricSuite>) -> Res<Self> {
69 Self::strip_unsupported(&mut symmetric, kem);
70 assert!(!symmetric.is_empty());
71 let (sk, pk) = generate_key_pair(kem)?;
72 Ok(Self {
73 key_id,
74 kem,
75 symmetric,
76 sk: Some(sk),
77 pk,
78 })
79 }
80
81 #[allow(unused)]
87 pub fn derive(
88 key_id: u8,
89 kem: Kem,
90 mut symmetric: Vec<SymmetricSuite>,
91 ikm: &[u8],
92 ) -> Res<Self> {
93 #[cfg(feature = "rust-hpke")]
94 {
95 Self::strip_unsupported(&mut symmetric, kem);
96 assert!(!symmetric.is_empty());
97 let (sk, pk) = derive_key_pair(kem, ikm)?;
98 Ok(Self {
99 key_id,
100 kem,
101 symmetric,
102 sk: Some(sk),
103 pk,
104 })
105 }
106 #[cfg(not(feature = "rust-hpke"))]
107 {
108 Err(Error::Unsupported)
109 }
110 }
111
112 pub fn encode_list(list: &[impl AsRef<Self>]) -> Res<Vec<u8>> {
123 let mut buf = Vec::new();
124 for c in list {
125 let offset = buf.len();
126 buf.write_u16::<NetworkEndian>(0)?;
127 c.as_ref().write(&mut buf)?;
128 let len = buf.len() - offset - 2;
129 buf[offset] = u8::try_from(len >> 8)?;
130 buf[offset + 1] = u8::try_from(len & 0xff).unwrap();
131 }
132 Ok(buf)
133 }
134
135 fn write(&self, buf: &mut Vec<u8>) -> Res<()> {
136 buf.write_u8(self.key_id)?;
137 buf.write_u16::<NetworkEndian>(u16::from(self.kem))?;
138 let pk_buf = self.pk.key_data()?;
139 buf.extend_from_slice(&pk_buf);
140 buf.write_u16::<NetworkEndian>((self.symmetric.len() * 4).try_into()?)?;
141 for s in &self.symmetric {
142 buf.write_u16::<NetworkEndian>(u16::from(s.kdf()))?;
143 buf.write_u16::<NetworkEndian>(u16::from(s.aead()))?;
144 }
145 Ok(())
146 }
147
148 pub fn encode(&self) -> Res<Vec<u8>> {
171 let mut buf = Vec::new();
172 self.write(&mut buf)?;
173 Ok(buf)
174 }
175
176 pub fn decode(encoded_config: &[u8]) -> Res<Self> {
179 let end_position = u64::try_from(encoded_config.len())?;
180 let mut r = Cursor::new(encoded_config);
181 let key_id = r.read_u8()?;
182 let kem = Kem::try_from(r.read_u16::<NetworkEndian>()?)?;
183
184 let kem_config = HpkeConfig::new(kem, Kdf::HkdfSha256, AeadId::Aes128Gcm);
186 if !kem_config.supported() {
187 return Err(Error::Unsupported);
188 }
189 let mut pk_buf = vec![0; kem_config.kem().n_pk()];
190 r.read_exact(&mut pk_buf)?;
191
192 let sym_len = r.read_u16::<NetworkEndian>()?;
193 let mut sym = vec![0; usize::from(sym_len)];
194 r.read_exact(&mut sym)?;
195 if sym.is_empty() || (sym.len() % 4 != 0) {
196 return Err(Error::Format);
197 }
198 let sym_count = sym.len() / 4;
199 let mut sym_r = BufReader::new(&sym[..]);
200 let mut symmetric = Vec::with_capacity(sym_count);
201 for _ in 0..sym_count {
202 let kdf = Kdf::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
203 let aead = AeadId::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
204 symmetric.push(SymmetricSuite::new(kdf, aead));
205 }
206
207 if r.position() != end_position {
209 return Err(Error::Format);
210 }
211
212 Self::strip_unsupported(&mut symmetric, kem);
213 let pk = HpkeR::decode_public_key(kem_config.kem(), &pk_buf)?;
214
215 Ok(Self {
216 key_id,
217 kem,
218 symmetric,
219 sk: None,
220 pk,
221 })
222 }
223
224 pub fn decode_list(encoded_list: &[u8]) -> Res<Vec<Self>> {
228 let end_position = u64::try_from(encoded_list.len())?;
229 let mut r = Cursor::new(encoded_list);
230 let mut configs = Vec::new();
231 loop {
232 if r.position() == end_position {
233 break;
234 }
235 let len = usize::from(r.read_u16::<NetworkEndian>()?);
236 let buf = r.fill_buf()?;
237 if len > buf.len() {
238 return Err(Error::Truncated);
239 }
240 let res = Self::decode(&buf[..len]);
241 r.consume(len);
242 match res {
243 Ok(config) => configs.push(config),
244 Err(Error::Unsupported) => continue,
245 Err(e) => return Err(e),
246 }
247 }
248 Ok(configs)
249 }
250
251 pub fn select(&self, sym: SymmetricSuite) -> Res<HpkeConfig> {
256 if self.symmetric.contains(&sym) {
257 let config = HpkeConfig::new(self.kem, sym.kdf(), sym.aead());
258 Ok(config)
259 } else {
260 Err(Error::Unsupported)
261 }
262 }
263}
264
265impl AsRef<Self> for KeyConfig {
266 fn as_ref(&self) -> &Self {
267 self
268 }
269}
270
271#[cfg(test)]
272mod test {
273 use crate::{
274 hpke::{Aead, Kdf, Kem},
275 init, Error, KeyConfig, KeyId, SymmetricSuite,
276 };
277 use std::iter::zip;
278
279 const KEY_ID: KeyId = 1;
280 const KEM: Kem = Kem::K256Sha256;
281 const SYMMETRIC: &[SymmetricSuite] = &[
282 SymmetricSuite::new(Kdf::HkdfSha256, Aead::Aes128Gcm),
283 SymmetricSuite::new(Kdf::HkdfSha256, Aead::ChaCha20Poly1305),
284 ];
285
286 #[test]
287 fn encode_decode_config_list() {
288 const COUNT: usize = 3;
289 init();
290
291 let mut configs = Vec::with_capacity(COUNT);
292 configs.resize_with(COUNT, || {
293 KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC)).unwrap()
294 });
295
296 let buf = KeyConfig::encode_list(&configs).unwrap();
297 let decoded_list = KeyConfig::decode_list(&buf).unwrap();
298 for (original, decoded) in zip(&configs, &decoded_list) {
299 assert_eq!(decoded.key_id, original.key_id);
300 assert_eq!(decoded.kem, original.kem);
301 assert_eq!(
302 decoded.pk.key_data().unwrap(),
303 original.pk.key_data().unwrap()
304 );
305 assert!(decoded.sk.is_none());
306 assert!(original.sk.is_some());
307 }
308
309 assert!(KeyConfig::decode_list(&buf[..buf.len() - 3]).is_err());
311 }
312
313 #[test]
314 fn empty_config_list() {
315 let list = KeyConfig::decode_list(&[]).unwrap();
316 assert!(list.is_empty());
317
318 let list = KeyConfig::decode_list(&[0, 3, 0, 0, 0]).unwrap();
323 assert!(list.is_empty());
324 }
325
326 #[test]
327 fn bad_config_list_length() {
328 init();
329
330 let res = KeyConfig::decode_list(&[0]);
332 assert!(matches!(res, Err(Error::Io(_))));
333 }
334
335 #[test]
336 fn decode_bad_config() {
337 init();
338
339 let mut x25519 = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC))
340 .unwrap()
341 .encode()
342 .unwrap();
343 {
344 let trunc = |n: usize| KeyConfig::decode(&x25519[..n]);
346
347 assert!(matches!(trunc(2), Err(Error::Io(_))));
349 assert!(matches!(trunc(4), Err(Error::Io(_))));
351 assert!(matches!(trunc(36), Err(Error::Io(_))));
353 assert!(matches!(trunc(38), Err(Error::Io(_))));
355 }
356
357 x25519.push(0);
359 assert!(matches!(KeyConfig::decode(&x25519), Err(Error::Format)));
360 }
361
362 #[test]
364 fn truncate_kdf_aead_list() {
365 init();
366
367 let mut x25519 = KeyConfig::new(KEY_ID, KEM, Vec::from(SYMMETRIC))
368 .unwrap()
369 .encode()
370 .unwrap();
371 x25519.truncate(38);
372 assert_eq!(usize::from(x25519[36]), SYMMETRIC.len() * 4);
373 x25519[36] = 1;
374 assert!(matches!(KeyConfig::decode(&x25519), Err(Error::Format)));
375 }
376}