/// The key configuration of a server. This can be used by both client and server. /// An important invariant of this structure is that it does not include /// any combination of KEM, KDF, and AEAD that is not supported. #[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone)] pubstruct KeyConfig { pub(crate) key_id: KeyId, pub(crate) kem: Kem, pub(crate) symmetric: Vec<SymmetricSuite>, pub(crate) sk: Option<PrivateKey>, pub(crate) pk: PublicKey,
}
/// Construct a configuration for the server side. /// # Panics /// If the configurations don't include a supported configuration. pubfn new(key_id: u8, kem: Kem, mut symmetric: Vec<SymmetricSuite>) -> Res<Self> { Self::strip_unsupported(&mut symmetric, kem);
assert!(!symmetric.is_empty()); let (sk, pk) = generate_key_pair(kem)?;
Ok(Self {
key_id,
kem,
symmetric,
sk: Some(sk),
pk,
})
}
/// Derive a configuration for the server side from input keying material, /// using the `DeriveKeyPair` functionality of the HPKE KEM defined here: /// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-12.html#section-4> /// # Panics /// If the configurations don't include a supported configuration. #[allow(unused)] pubfn derive(
key_id: u8,
kem: Kem, mut symmetric: Vec<SymmetricSuite>,
ikm: &[u8],
) -> Res<Self> { #[cfg(feature = "rust-hpke")]
{ Self::strip_unsupported(&mut symmetric, kem);
assert!(!symmetric.is_empty()); let (sk, pk) = derive_key_pair(kem, ikm)?;
Ok(Self {
key_id,
kem,
symmetric,
sk: Some(sk),
pk,
})
} #[cfg(not(feature = "rust-hpke"))]
{
Err(Error::Unsupported)
}
}
/// Encode a list of key configurations. /// /// This produces the key configuration format that is used for /// the "application/ohttp-keys" media type. /// Each item in the list is written as per [`encode()`]. /// /// # Panics /// Not as a result of this function. /// /// [`encode()`]: Self::encode pubfn encode_list(list: &[impl AsRef<Self>]) -> Res<Vec<u8>> { letmut buf = Vec::new(); for c in list { let offset = buf.len();
buf.write_u16::<NetworkEndian>(0)?;
c.as_ref().write(&mut buf)?; let len = buf.len() - offset - 2;
buf[offset] = u8::try_from(len >> 8)?;
buf[offset + 1] = u8::try_from(len & 0xff).unwrap();
}
Ok(buf)
}
fn write(&self, buf: &mut Vec<u8>) -> Res<()> {
buf.write_u8(self.key_id)?;
buf.write_u16::<NetworkEndian>(u16::from(self.kem))?; let pk_buf = self.pk.key_data()?;
buf.extend_from_slice(&pk_buf);
buf.write_u16::<NetworkEndian>((self.symmetric.len() * 4).try_into()?)?; for s in &self.symmetric {
buf.write_u16::<NetworkEndian>(u16::from(s.kdf()))?;
buf.write_u16::<NetworkEndian>(u16::from(s.aead()))?;
}
Ok(())
}
/// Encode into a wire format. This shares a format with the core of ECH: /// /// ```tls-format /// opaque HpkePublicKey[Npk]; /// uint16 HpkeKemId; // Defined in I-D.irtf-cfrg-hpke /// uint16 HpkeKdfId; // Defined in I-D.irtf-cfrg-hpke /// uint16 HpkeAeadId; // Defined in I-D.irtf-cfrg-hpke /// /// struct { /// HpkeKdfId kdf_id; /// HpkeAeadId aead_id; /// } ECHCipherSuite; /// /// struct { /// uint8 key_id; /// HpkeKemId kem_id; /// HpkePublicKey public_key; /// ECHCipherSuite cipher_suites<4..2^16-4>; /// } ECHKeyConfig; /// ``` /// # Panics /// Not as a result of this function. pubfn encode(&self) -> Res<Vec<u8>> { letmut buf = Vec::new(); self.write(&mut buf)?;
Ok(buf)
}
/// Construct a configuration from the encoded server configuration. /// The format of `encoded_config` is the output of `Self::encode`. pubfn decode(encoded_config: &[u8]) -> Res<Self> { let end_position = u64::try_from(encoded_config.len())?; letmut r = Cursor::new(encoded_config); let key_id = r.read_u8()?; let kem = Kem::try_from(r.read_u16::<NetworkEndian>()?)?;
// Note that the KDF and AEAD doesn't matter here. let kem_config = HpkeConfig::new(kem, Kdf::HkdfSha256, AeadId::Aes128Gcm); if !kem_config.supported() { return Err(Error::Unsupported);
} letmut pk_buf = vec![0; kem_config.kem().n_pk()];
r.read_exact(&mut pk_buf)?;
let sym_len = r.read_u16::<NetworkEndian>()?; letmut sym = vec![0; usize::from(sym_len)];
r.read_exact(&mut sym)?; if sym.is_empty() || (sym.len() % 4 != 0) { return Err(Error::Format);
} let sym_count = sym.len() / 4; letmut sym_r = BufReader::new(&sym[..]); letmut symmetric = Vec::with_capacity(sym_count); for _ in0..sym_count { let kdf = Kdf::try_from(sym_r.read_u16::<NetworkEndian>()?)?; let aead = AeadId::try_from(sym_r.read_u16::<NetworkEndian>()?)?;
symmetric.push(SymmetricSuite::new(kdf, aead));
}
// Check that there was nothing extra and we are at the end of the buffer. if r.position() != end_position { return Err(Error::Format);
}
Self::strip_unsupported(&mut symmetric, kem); let pk = HpkeR::decode_public_key(kem_config.kem(), &pk_buf)?;
/// Decode a list of key configurations. /// This only returns the valid and supported key configurations; /// unsupported configurations are dropped silently. pubfn decode_list(encoded_list: &[u8]) -> Res<Vec<Self>> { let end_position = u64::try_from(encoded_list.len())?; letmut r = Cursor::new(encoded_list); letmut configs = Vec::new(); loop { if r.position() == end_position { break;
} let len = usize::from(r.read_u16::<NetworkEndian>()?); let buf = r.fill_buf()?; if len > buf.len() { return Err(Error::Truncated);
} let res = Self::decode(&buf[..len]);
r.consume(len); match res {
Ok(config) => configs.push(config),
Err(Error::Unsupported) => continue,
Err(e) => return Err(e),
}
}
Ok(configs)
}
/// Select creates a new configuration that contains the identified symmetric suite. /// /// # Errors /// If the given suite is not supported by this configuration. pubfn select(&self, sym: SymmetricSuite) -> Res<HpkeConfig> { ifself.symmetric.contains(&sym) { let config = HpkeConfig::new(self.kem, sym.kdf(), sym.aead());
Ok(config)
} else {
Err(Error::Unsupported)
}
}
}
let buf = KeyConfig::encode_list(&configs).unwrap(); let decoded_list = KeyConfig::decode_list(&buf).unwrap(); for (original, decoded) in zip(&configs, &decoded_list) {
assert_eq!(decoded.key_id, original.key_id);
assert_eq!(decoded.kem, original.kem);
assert_eq!(
decoded.pk.key_data().unwrap(),
original.pk.key_data().unwrap()
);
assert!(decoded.sk.is_none());
assert!(original.sk.is_some());
}
// Check that truncation errors in `KeyConfig::decode` are caught.
assert!(KeyConfig::decode_list(&buf[..buf.len() - 3]).is_err());
}
#[test] fn empty_config_list() { let list = KeyConfig::decode_list(&[]).unwrap();
assert!(list.is_empty());
// A reserved KEM ID is not bad. Note that we don't check that the data // following the KEM ID is even the minimum length, allowing this to be // zero bytes, where you need at least some bytes in a public key and some // bytes to identify at least one KDF and AEAD (i.e., more than 6 bytes). let list = KeyConfig::decode_list(&[0, 3, 0, 0, 0]).unwrap();
assert!(list.is_empty());
}
#[test] fn bad_config_list_length() {
init();
// A one byte length for a config. let res = KeyConfig::decode_list(&[0]);
assert!(matches!(res, Err(Error::Io(_))));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.