Implement serde serialization and deserialization to follow Rust's standards.
This commit is contained in:
committed by
Kevin Lewi
parent
940d1dcdb2
commit
055e76a115
Generated
+11
@@ -40,6 +40,15 @@ version = "0.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -562,6 +571,7 @@ version = "0.5.1-pre.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
"bincode",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
"criterion",
|
"criterion",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
@@ -577,6 +587,7 @@ dependencies = [
|
|||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
"scrypt",
|
"scrypt",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
|||||||
+5
-1
@@ -10,13 +10,15 @@ edition = "2018"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["u64_backend"]
|
default = ["u64_backend", "serialize"]
|
||||||
slow-hash = ["scrypt"]
|
slow-hash = ["scrypt"]
|
||||||
bench = []
|
bench = []
|
||||||
u64_backend = ["curve25519-dalek/u64_backend"]
|
u64_backend = ["curve25519-dalek/u64_backend"]
|
||||||
u32_backend = ["curve25519-dalek/u32_backend"]
|
u32_backend = ["curve25519-dalek/u32_backend"]
|
||||||
|
serialize = ["serde", "base64"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
base64 = { version = "0.13", optional = true }
|
||||||
curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] }
|
curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] }
|
||||||
digest = "0.9.0"
|
digest = "0.9.0"
|
||||||
displaydoc = "0.1.7"
|
displaydoc = "0.1.7"
|
||||||
@@ -26,6 +28,7 @@ hkdf = "0.10.0"
|
|||||||
hmac = "0.10.1"
|
hmac = "0.10.1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
scrypt = { version = "0.5.0", optional = true }
|
scrypt = { version = "0.5.0", optional = true }
|
||||||
|
serde = { version = "1", optional = true }
|
||||||
subtle = { version = "2.3.0", default-features = false }
|
subtle = { version = "2.3.0", default-features = false }
|
||||||
thiserror = "1.0.22"
|
thiserror = "1.0.22"
|
||||||
zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
|
zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
|
||||||
@@ -33,6 +36,7 @@ zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.35"
|
anyhow = "1.0.35"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
|
bincode = "1"
|
||||||
chacha20poly1305 = "0.7.1"
|
chacha20poly1305 = "0.7.1"
|
||||||
criterion = "0.3.3"
|
criterion = "0.3.3"
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::{
|
|||||||
PakeError, ProtocolError,
|
PakeError, ProtocolError,
|
||||||
},
|
},
|
||||||
group::Group,
|
group::Group,
|
||||||
|
impl_serialize_and_deserialize_for,
|
||||||
key_exchange::traits::{KeyExchange, ToBytes},
|
key_exchange::traits::{KeyExchange, ToBytes},
|
||||||
keypair::{Key, KeyPair, SizedBytesExt},
|
keypair::{Key, KeyPair, SizedBytesExt},
|
||||||
};
|
};
|
||||||
@@ -47,6 +48,8 @@ impl<CS: CipherSuite> RegistrationRequest<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(RegistrationRequest);
|
||||||
|
|
||||||
/// The answer sent by the server to the user, upon reception of the
|
/// The answer sent by the server to the user, upon reception of the
|
||||||
/// registration attempt
|
/// registration attempt
|
||||||
pub struct RegistrationResponse<CS: CipherSuite> {
|
pub struct RegistrationResponse<CS: CipherSuite> {
|
||||||
@@ -81,6 +84,8 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(RegistrationResponse);
|
||||||
|
|
||||||
/// The final message from the client, containing sealed cryptographic
|
/// The final message from the client, containing sealed cryptographic
|
||||||
/// identifiers
|
/// identifiers
|
||||||
pub struct RegistrationUpload<CS: CipherSuite> {
|
pub struct RegistrationUpload<CS: CipherSuite> {
|
||||||
@@ -122,6 +127,8 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(RegistrationUpload);
|
||||||
|
|
||||||
/// The message sent by the user to the server, to initiate registration
|
/// The message sent by the user to the server, to initiate registration
|
||||||
pub struct CredentialRequest<CS: CipherSuite> {
|
pub struct CredentialRequest<CS: CipherSuite> {
|
||||||
/// blinded password information
|
/// blinded password information
|
||||||
@@ -159,6 +166,8 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(CredentialRequest);
|
||||||
|
|
||||||
/// The answer sent by the server to the user, upon reception of the
|
/// The answer sent by the server to the user, upon reception of the
|
||||||
/// login attempt
|
/// login attempt
|
||||||
pub struct CredentialResponse<CS: CipherSuite> {
|
pub struct CredentialResponse<CS: CipherSuite> {
|
||||||
@@ -229,6 +238,8 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(CredentialResponse);
|
||||||
|
|
||||||
/// The answer sent by the client to the server, upon reception of the
|
/// The answer sent by the client to the server, upon reception of the
|
||||||
/// sealed envelope
|
/// sealed envelope
|
||||||
pub struct CredentialFinalization<CS: CipherSuite> {
|
pub struct CredentialFinalization<CS: CipherSuite> {
|
||||||
@@ -248,3 +259,5 @@ impl<CS: CipherSuite> CredentialFinalization<CS> {
|
|||||||
Ok(Self { ke3_message })
|
Ok(Self { ke3_message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(CredentialFinalization);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::{
|
|||||||
errors::{utils::check_slice_size_atleast, InternalPakeError, PakeError, ProtocolError},
|
errors::{utils::check_slice_size_atleast, InternalPakeError, PakeError, ProtocolError},
|
||||||
group::Group,
|
group::Group,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
impl_serialize_and_deserialize_for,
|
||||||
key_exchange::traits::{KeyExchange, ToBytesWithPointers},
|
key_exchange::traits::{KeyExchange, ToBytesWithPointers},
|
||||||
keypair::{Key, KeyPair, SizedBytesExt},
|
keypair::{Key, KeyPair, SizedBytesExt},
|
||||||
map_to_curve::GroupWithMapToCurve,
|
map_to_curve::GroupWithMapToCurve,
|
||||||
@@ -83,6 +84,8 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(ClientRegistration);
|
||||||
|
|
||||||
/// Optional parameters for client registration finish
|
/// Optional parameters for client registration finish
|
||||||
pub enum ClientRegistrationFinishParameters {
|
pub enum ClientRegistrationFinishParameters {
|
||||||
/// Specifying the identifiers idU and idS (corresponding to custom identifier mode)
|
/// Specifying the identifiers idU and idS (corresponding to custom identifier mode)
|
||||||
@@ -385,6 +388,8 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(ServerRegistration);
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
// =====
|
// =====
|
||||||
|
|
||||||
@@ -454,6 +459,8 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(ClientLogin);
|
||||||
|
|
||||||
/// Optional parameters for client login start
|
/// Optional parameters for client login start
|
||||||
pub enum ClientLoginStartParameters {
|
pub enum ClientLoginStartParameters {
|
||||||
/// Specifying a plaintext info field that will be sent to the server
|
/// Specifying a plaintext info field that will be sent to the server
|
||||||
@@ -880,6 +887,8 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_serialize_and_deserialize_for!(ServerLogin);
|
||||||
|
|
||||||
// Zeroize on drop implementations
|
// Zeroize on drop implementations
|
||||||
|
|
||||||
// This can't be derived because of the use of a phantom parameter
|
// This can't be derived because of the use of a phantom parameter
|
||||||
|
|||||||
@@ -53,5 +53,74 @@ pub(crate) fn tokenize(input: &[u8], size_bytes: usize) -> Result<(Vec<u8>, Vec<
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_serialize_and_deserialize_for {
|
||||||
|
($t:ident) => {
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<CS: CipherSuite> serde::Serialize for $t<CS> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
serializer.serialize_str(&base64::encode(&self.serialize()))
|
||||||
|
} else {
|
||||||
|
serializer.serialize_bytes(&self.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t<CS> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
let s = <&str>::deserialize(deserializer)?;
|
||||||
|
$t::<CS>::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
|
||||||
|
.map_err(serde::de::Error::custom)
|
||||||
|
} else {
|
||||||
|
struct ByteVisitor<CS: CipherSuite> {
|
||||||
|
marker: std::marker::PhantomData<CS>,
|
||||||
|
}
|
||||||
|
impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor<CS> {
|
||||||
|
type Value = $t<CS>;
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
formatter.write_str(std::concat!(
|
||||||
|
"the byte representation of a ",
|
||||||
|
std::stringify!($t)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
$t::<CS>::deserialize(value).map_err(|_| {
|
||||||
|
serde::de::Error::invalid_value(
|
||||||
|
serde::de::Unexpected::Bytes(value),
|
||||||
|
&std::concat!(
|
||||||
|
"invalid byte sequence for ",
|
||||||
|
std::stringify!($t)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_bytes(ByteVisitor::<CS> {
|
||||||
|
marker: std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
@@ -450,6 +450,42 @@ fn test_registration_request() -> Result<(), ProtocolError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[test]
|
||||||
|
fn test_serialization() -> Result<(), ProtocolError> {
|
||||||
|
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||||
|
let mut rng = CycleRng::new(parameters.blinding_factor.to_vec());
|
||||||
|
let client_registration_start_result =
|
||||||
|
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut rng, ¶meters.password)?;
|
||||||
|
{
|
||||||
|
// Test the json serialization (human-readable, base64).
|
||||||
|
let registration_request_json =
|
||||||
|
serde_json::to_string(&client_registration_start_result.message).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
registration_request_json,
|
||||||
|
r#""FLqG5TAYzlUH0r+y2YrT9g4wLYJr/zQQpexmnI4e8X0=""#
|
||||||
|
);
|
||||||
|
let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> =
|
||||||
|
serde_json::from_str(®istration_request_json).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
hex::encode(client_registration_start_result.message.serialize()),
|
||||||
|
hex::encode(registration_request.serialize()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Test the bincode serialization (binary).
|
||||||
|
let registration_request_bin =
|
||||||
|
bincode::serialize(&client_registration_start_result.message).unwrap();
|
||||||
|
assert_eq!(registration_request_bin.len(), 40);
|
||||||
|
let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> =
|
||||||
|
bincode::deserialize(®istration_request_bin).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
hex::encode(client_registration_start_result.message.serialize()),
|
||||||
|
hex::encode(registration_request.serialize()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_registration_response() -> Result<(), ProtocolError> {
|
fn test_registration_response() -> Result<(), ProtocolError> {
|
||||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||||
|
|||||||
Reference in New Issue
Block a user