diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e114d14..8f7529c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,12 +49,6 @@ jobs: RUSTFLAGS: -D warnings -A dead_code -A unused_imports run: cargo test --no-default-features --features="p384" - - name: Run cargo test with X25519 and serde impls enabled - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: -D warnings -A dead_code -A unused_imports - run: cargo test --no-default-features --features="x25519,serde_impls" - - name: Run cargo test with all features enabled env: CARGO_INCREMENTAL: 0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 563c274..d468635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Pending * Added `Serializable::write_exact` so serialization requires less stack space +* Removed all impls of `serde::{Serialize, Deserailize}` from crate ## [0.11.0] - 2023-10-11 diff --git a/Cargo.toml b/Cargo.toml index 35eb05b..65b3bdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,6 @@ default = ["alloc", "p256", "x25519"] x25519 = ["dep:x25519-dalek"] p384 = ["dep:p384"] p256 = ["dep:p256"] -# Include serde Serialize/Deserialize impls for all relevant types -serde_impls = ["serde", "generic-array/serde"] # Include allocating methods like open() and seal() alloc = [] # Includes an implementation of `std::error::Error` for `HpkeError`. Also does what `alloc` does. @@ -39,7 +37,6 @@ rand_core = { version = "0.6", default-features = false } p256 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdh"], optional = true} p384 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdh"], optional = true} sha2 = { version = "0.10", default-features = false } -serde = { version = "1.0", default-features = false, optional = true } subtle = { version = "2.5", default-features = false } x25519-dalek = { version = "2", default-features = false, features = ["static_secrets"], optional = true } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } diff --git a/README.md b/README.md index 454939f..1287ab3 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,6 @@ Feature flag list: * `x25519` - Enables X25519-based KEMs * `p256` - Enables NIST P-256-based KEMs * `p384` - Enables NIST P-384-based KEMs -* `serde_impls` - Includes implementations of `serde::Serialize` and `serde::Deserialize` for all `hpke::Serializable` and `hpke::Deserializable` types * `std` - Includes an implementation of `std::error::Error` for `HpkeError`. Also does what `alloc` does. For info on how to omit or include feature flags, see the [cargo docs on features](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features). @@ -61,6 +60,13 @@ Usage Examples See the [client-server](examples/client_server.rs) example for an idea of how to use HPKE. +Breaking changes +---------------- + +### Breaking changes in v0.12 + +The `serde_impls` feature was removed. If you were using this and require backwards compatible serialization/deserialization, see the wiki page [here](https://github.com/rozbb/rust-hpke/wiki/Migrating-away-from-the-%60serde_impls%60-feature). + MSRV ---- diff --git a/src/dhkex.rs b/src/dhkex.rs index 3eab3b5..2a3e29c 100644 --- a/src/dhkex.rs +++ b/src/dhkex.rs @@ -2,9 +2,6 @@ use crate::{kdf::Kdf as KdfTrait, util::KemSuiteId, Deserializable, Serializable use core::fmt::Debug; -#[cfg(feature = "serde_impls")] -use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; - // This is the maximum value of all of Npk, Ndh, and Nenc. It's achieved by P-521 in RFC 9180 ยง7.1 // Table 2. pub(crate) const MAX_PUBKEY_SIZE: usize = 133; @@ -18,37 +15,12 @@ pub struct DhError; /// way to generate keypairs, perform the Diffie-Hellman operation, and serialize/deserialize /// pubkeys. This is built into a KEM in `kem/dhkem.rs`. pub trait DhKeyExchange { - // Public and private keys need to implement serde::{Serialize, Deserialize} if the serde_impls - // feature is set. So double up all the definitions: one with serde and one without. - - /// The key exchange's public key type. If you want to generate a keypair, see - /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(feature = "serde_impls")] - type PublicKey: Clone - + Debug - + PartialEq - + Eq - + Serializable - + Deserializable - + SerdeSerialize - + for<'a> SerdeDeserialize<'a>; /// The key exchange's public key type. If you want to generate a keypair, see /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(not(feature = "serde_impls"))] type PublicKey: Clone + Debug + PartialEq + Eq + Serializable + Deserializable; /// The key exchange's private key type. If you want to generate a keypair, see /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(feature = "serde_impls")] - type PrivateKey: Clone - + Serializable - + Deserializable - + SerdeSerialize - + for<'a> SerdeDeserialize<'a>; - - /// The key exchange's private key type. If you want to generate a keypair, see - /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(not(feature = "serde_impls"))] type PrivateKey: Clone + Serializable + Deserializable; /// The result of a DH operation diff --git a/src/kem.rs b/src/kem.rs index c44697c..532714c 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -11,57 +11,20 @@ use zeroize::Zeroize; mod dhkem; pub use dhkem::*; -#[cfg(feature = "serde_impls")] -use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; - /// Represents authenticated encryption functionality pub trait Kem: Sized { /// The key exchange's public key type. If you want to generate a keypair, see /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(feature = "serde_impls")] - type PublicKey: Clone - + Debug - + PartialEq - + Eq - + Serializable - + Deserializable - + SerdeSerialize - + for<'a> SerdeDeserialize<'a>; - /// The key exchange's public key type. If you want to generate a keypair, see - /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(not(feature = "serde_impls"))] type PublicKey: Clone + Debug + PartialEq + Eq + Serializable + Deserializable; /// The key exchange's private key type. If you want to generate a keypair, see /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(feature = "serde_impls")] - type PrivateKey: Clone - + PartialEq - + Eq - + Serializable - + Deserializable - + SerdeSerialize - + for<'a> SerdeDeserialize<'a>; - - /// The key exchange's private key type. If you want to generate a keypair, see - /// `Kem::gen_keypair` or `Kem::derive_keypair` - #[cfg(not(feature = "serde_impls"))] type PrivateKey: Clone + PartialEq + Eq + Serializable + Deserializable; /// Computes the public key of a given private key fn sk_to_pk(sk: &Self::PrivateKey) -> Self::PublicKey; - - /// The encapsulated key for this KEM. This is used by the recipient to derive the shared - /// secret. - #[cfg(feature = "serde_impls")] - type EncappedKey: Clone - + Serializable - + Deserializable - + SerdeSerialize - + for<'a> SerdeDeserialize<'a>; /// The encapsulated key for this KEM. This is used by the recipient to derive the shared /// secret. - #[cfg(not(feature = "serde_impls"))] type EncappedKey: Clone + Serializable + Deserializable; /// The size of a shared secret in this KEM diff --git a/src/lib.rs b/src/lib.rs index 69bc7c2..af55107 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,9 +126,6 @@ mod op_mode; mod setup; mod single_shot; -#[cfg(feature = "serde_impls")] -mod serde_impls; - #[doc(inline)] pub use kem::Kem; #[doc(inline)] diff --git a/src/serde_impls.rs b/src/serde_impls.rs deleted file mode 100644 index bd0e763..0000000 --- a/src/serde_impls.rs +++ /dev/null @@ -1,175 +0,0 @@ -//! This module defines serde::Serialize and serde::Deserialize for all Serializable and -//! Deserializable types defined in this crate. This is gated under the `serde_impls` feature. - -use crate::{ - aead::{Aead, AeadTag}, - dhkex, kem, Deserializable, Serializable, -}; - -use digest::generic_array::GenericArray; -use serde::{de::Error, Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; - -// Implement Serialize for AeadTag -impl SerdeSerialize for AeadTag { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - // Convert to a GenericArray and serialize that - let bytes = self.to_bytes(); - bytes.serialize(serializer) - } -} - -// Implement Deserialize for AeadTag -impl<'de, A: Aead> SerdeDeserialize<'de> for AeadTag { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - // Use the GenericArray deserializer to get the appropriate number of bytes - let bytes = GenericArray::::OutputSize>::deserialize( - deserializer, - )?; - // Try to build this object from the given bytes. If it doesn't work, wrap and - // return the resulting HpkeError - Self::from_bytes(&bytes).map_err(D::Error::custom) - } -} - -// Implements serde::{Serialize, Deserialize} over type t. This is almost identical to above. -macro_rules! impl_serde_noparam { - ($t:ty) => { - /// Implements `serde::Serialize` - impl SerdeSerialize for $t { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - // Convert to a GenericArray and serialize that - let bytes = self.to_bytes(); - bytes.serialize(serializer) - } - } - - /// Implements `serde::Deserialize` - impl<'de> SerdeDeserialize<'de> for $t { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - // Use the GenericArray deserializer to get the appropriate number of bytes - let bytes = - GenericArray::::OutputSize>::deserialize( - deserializer, - )?; - // Try to build this object from the given bytes. If it doesn't work, wrap and - // return the resulting HpkeError - Self::from_bytes(&bytes).map_err(D::Error::custom) - } - } - }; -} - -// Implement Serialize/Deserialize for all PrivateKey, PublicKey, and EncappedKey types, as -// features permit - -#[cfg(feature = "x25519")] -impl_serde_noparam!(dhkex::x25519::PrivateKey); -#[cfg(feature = "x25519")] -impl_serde_noparam!(dhkex::x25519::PublicKey); -#[cfg(feature = "x25519")] -impl_serde_noparam!(kem::x25519_hkdfsha256::EncappedKey); - -#[cfg(feature = "p256")] -impl_serde_noparam!(dhkex::ecdh_nistp::p256::PrivateKey); -#[cfg(feature = "p256")] -impl_serde_noparam!(dhkex::ecdh_nistp::p256::PublicKey); -#[cfg(feature = "p256")] -impl_serde_noparam!(kem::dhp256_hkdfsha256::EncappedKey); - -#[cfg(feature = "p384")] -impl_serde_noparam!(dhkex::ecdh_nistp::p384::PrivateKey); -#[cfg(feature = "p384")] -impl_serde_noparam!(dhkex::ecdh_nistp::p384::PublicKey); -#[cfg(feature = "p384")] -impl_serde_noparam!(kem::dhp384_hkdfsha384::EncappedKey); - -#[cfg(test)] -mod test { - use crate::{ - aead::AesGcm128, - kdf::HkdfSha256, - kem::Kem as KemTrait, - setup_sender, - test_util::{gen_rand_buf, new_op_mode_pair, OpModeKind}, - Serializable, - }; - - use rand::{rngs::StdRng, SeedableRng}; - use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; - - // Checks that serializing and deserializing the given data preserves its identity - fn assert_serde_roundtrip(data: &T) - where - T: Serializable + SerdeSerialize + for<'a> SerdeDeserialize<'a>, - { - // Write to JSON then try to read back from it - let json = serde_json::to_vec(data).expect("couldn't serialize data"); - let json_ref: &[u8] = json.as_ref(); - let reconstructed_data: T = - serde_json::from_reader(json_ref).expect("couldn't deserialize data"); - - // We actually have no way of telling these are equal besides just using our built-in - // Serialize functions and comparing the bytes. Maybe this is tautological. Maybe it's shut - // up. - assert_eq!(data.to_bytes(), reconstructed_data.to_bytes()); - } - - /// Tests that the serde's deserialize function undoes whatever serde's serialize function does - macro_rules! test_serde_roundtrip { - ($test_name:ident, $kem:ty) => { - #[test] - fn $test_name() { - type A = AesGcm128; - type Kdf = HkdfSha256; - type Kem = $kem; - - let mut csprng = StdRng::from_entropy(); - - let info = b"my wallet"; - let mut plaintext = *b"DJ turn it up, that's my track right there"; - - // Do a whole bunch of stuff to generate a valid session. All we care about is that - // this gives us a pubkey, secret key, and encapped key to test serde on - let (sk_recip, pk_recip) = Kem::gen_keypair(&mut csprng); - let (psk, psk_id) = (gen_rand_buf(), gen_rand_buf()); - let (sender_mode, _) = - new_op_mode_pair::(OpModeKind::Base, &psk, &psk_id); - let (encapped_key, mut aead_ctx) = - setup_sender::(&sender_mode, &pk_recip, &info[..], &mut csprng) - .unwrap(); - let aead_tag = aead_ctx - .seal_in_place_detached(&mut plaintext, b"") - .unwrap(); - - // Now see if serde behaves properly on everything that can be serialized - assert_serde_roundtrip(&sk_recip); - assert_serde_roundtrip(&pk_recip); - assert_serde_roundtrip(&encapped_key); - assert_serde_roundtrip(&aead_tag); - } - }; - } - - #[cfg(feature = "x25519")] - test_serde_roundtrip!(test_serde_roundtrip_x25519, crate::kem::X25519HkdfSha256); - - #[cfg(feature = "p256")] - test_serde_roundtrip!(test_serde_roundtrip_p256, crate::kem::DhP256HkdfSha256); - - #[cfg(feature = "p384")] - test_serde_roundtrip!(test_serde_roundtrip_p384, crate::kem::DhP384HkdfSha384); -}