From 1f3119e55223591cd80472b82e6b6c7da6a05335 Mon Sep 17 00:00:00 2001 From: Demilade Sonuga Date: Thu, 5 Dec 2024 16:08:31 +0100 Subject: [PATCH] Added serde support --- .github/workflows/dusk_ci.yaml | 4 +- Cargo.toml | 8 ++ src/lib.rs | 3 + src/serde_support.rs | 235 +++++++++++++++++++++++++++++++++ tests/serde.rs | 201 ++++++++++++++++++++++++++++ 5 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 src/serde_support.rs create mode 100644 tests/serde.rs diff --git a/.github/workflows/dusk_ci.yaml b/.github/workflows/dusk_ci.yaml index 4d6119d..b72b9fa 100644 --- a/.github/workflows/dusk_ci.yaml +++ b/.github/workflows/dusk_ci.yaml @@ -8,7 +8,7 @@ jobs: uses: dusk-network/.github/.github/workflows/code-analysis.yml@main with: clippy_default: false - clippy_args: --features=rkyv/size_32,zk + clippy_args: --features=rkyv/size_32,zk,serde dusk_analyzer: name: Dusk Analyzer @@ -26,7 +26,7 @@ jobs: name: Nightly tests uses: dusk-network/.github/.github/workflows/run-tests.yml@main with: - test_flags: --features=zk,alloc + test_flags: --features=zk,alloc,serde test_nightly_multisig_compiles: name: Nightly multisig tests compile diff --git a/Cargo.toml b/Cargo.toml index d2a15ee..2e0bc98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,9 @@ zeroize = { version = "1", default-features = false, features = ["derive"] } rkyv = { version = "0.7", optional = true, default-features = false } bytecheck = { version = "0.6", optional = true, default-features = false } dusk-plonk = { version = "0.20", default-features = false, features = ["alloc"], optional = true } +serde = { version = "1.0", optional = true } +bs58 = { version = "0.4" , optional = true } +serde_json = { version = "1.0", optional = true } [dev-dependencies] rkyv = { version = "0.7", default-features = false, features = ["size_32"] } @@ -62,6 +65,11 @@ rkyv-impl = [ "rkyv", "bytecheck", ] +serde = [ + "dep:serde", + "bs58", + "serde_json" +] [[test]] name = "gadgets" diff --git a/src/lib.rs b/src/lib.rs index 37c95b2..8a3a3ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,9 @@ mod signatures; #[cfg(feature = "zk")] pub mod gadgets; +#[cfg(feature = "serde")] +mod serde_support; + pub use error::Error; pub use keys::public::PublicKey; pub use keys::secret::SecretKey; diff --git a/src/serde_support.rs b/src/serde_support.rs new file mode 100644 index 0000000..d4c5a53 --- /dev/null +++ b/src/serde_support.rs @@ -0,0 +1,235 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +extern crate alloc; + +use alloc::format; +use alloc::string::String; + +use dusk_bytes::Serializable; +use serde::de::Error as SerdeError; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::{ + PublicKey, PublicKeyDouble, PublicKeyVarGen, SecretKey, SecretKeyVarGen, + Signature, SignatureDouble, SignatureVarGen, +}; + +impl Serialize for PublicKey { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + PublicKey::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for SecretKey { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for SecretKey { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + SecretKey::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for Signature { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + Signature::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for PublicKeyDouble { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for PublicKeyDouble { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + PublicKeyDouble::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for SignatureDouble { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for SignatureDouble { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + SignatureDouble::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for PublicKeyVarGen { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for PublicKeyVarGen { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + PublicKeyVarGen::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for SecretKeyVarGen { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for SecretKeyVarGen { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + SecretKeyVarGen::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} + +impl Serialize for SignatureVarGen { + fn serialize( + &self, + serializer: S, + ) -> Result { + let s = bs58::encode(self.to_bytes()).into_string(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for SignatureVarGen { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = String::deserialize(deserializer)?; + let decoded = + bs58::decode(&s).into_vec().map_err(SerdeError::custom)?; + let decoded_len = decoded.len(); + let byte_length_str = format!("{}", Self::SIZE); + let bytes: [u8; Self::SIZE] = decoded.try_into().map_err(|_| { + SerdeError::invalid_length(decoded_len, &byte_length_str.as_str()) + })?; + SignatureVarGen::from_bytes(&bytes) + .map_err(|err| SerdeError::custom(format!("{err:?}"))) + } +} diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 0000000..91f67d0 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,201 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +#![cfg(feature = "serde")] + +use dusk_bls12_381::BlsScalar; +use ff::Field; +use jubjub_schnorr::{ + PublicKey, PublicKeyDouble, PublicKeyVarGen, SecretKey, SecretKeyVarGen, + Signature, SignatureDouble, SignatureVarGen, +}; +use rand::rngs::StdRng; +use rand::SeedableRng; + +#[test] +fn public_key() { + let mut rng = StdRng::seed_from_u64(2321u64); + let pk = PublicKey::from(&SecretKey::random(&mut rng)); + let ser = serde_json::to_string(&pk).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(pk, deser); +} + +#[test] +fn secret_key() { + let mut rng = StdRng::seed_from_u64(2321u64); + let sk = SecretKey::random(&mut rng); + let ser = serde_json::to_string(&sk).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(sk, deser); +} + +#[test] +fn signature() { + let mut rng = StdRng::seed_from_u64(2321u64); + let sk = SecretKey::random(&mut rng); + let msg = BlsScalar::random(&mut rng); + let sig = sk.sign(&mut rng, msg); + let ser = serde_json::to_string(&sig).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(sig, deser); +} + +#[test] +fn public_key_double() { + let mut rng = StdRng::seed_from_u64(2321u64); + let sk = SecretKey::random(&mut rng); + let pk = PublicKeyDouble::from(&sk); + let ser = serde_json::to_string(&pk).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(pk, deser); +} + +#[test] +fn signature_double() { + let mut rng = StdRng::seed_from_u64(2321u64); + let sk = SecretKey::random(&mut rng); + let msg = BlsScalar::random(&mut rng); + let sig = sk.sign_double(&mut rng, msg); + let ser = serde_json::to_string(&sig).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(sig, deser); +} + +#[test] +fn public_key_var_gen() { + let mut rng = StdRng::seed_from_u64(2321u64); + let pk = PublicKeyVarGen::from(&SecretKeyVarGen::random(&mut rng)); + let ser = serde_json::to_string(&pk).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(pk, deser); +} + +#[test] +fn secret_key_var_gen() { + let mut rng = StdRng::seed_from_u64(2321u64); + let sk = SecretKeyVarGen::random(&mut rng); + let ser = serde_json::to_string(&sk).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(sk, deser); +} + +#[test] +fn signature_var_gen() { + let mut rng = StdRng::seed_from_u64(2321u64); + let sk = SecretKeyVarGen::random(&mut rng); + let msg = BlsScalar::random(&mut rng); + let sig = sk.sign(&mut rng, msg); + let ser = serde_json::to_string(&sig).unwrap(); + let deser = serde_json::from_str(&ser).unwrap(); + assert_eq!(sig, deser); +} + +#[test] +fn wrong_encoded() { + let wrong_encoded = "wrong-encoded"; + let public_key: Result = serde_json::from_str(&wrong_encoded); + println!("{public_key:?}"); + assert!(public_key.is_err()); + + let secret_key: Result = serde_json::from_str(&wrong_encoded); + assert!(secret_key.is_err()); + + let signature: Result = serde_json::from_str(&wrong_encoded); + assert!(signature.is_err()); + + let public_key_double: Result = + serde_json::from_str(&wrong_encoded); + assert!(public_key_double.is_err()); + + let signature_double: Result = + serde_json::from_str(&wrong_encoded); + assert!(signature_double.is_err()); + + let public_key_var_gen: Result = + serde_json::from_str(&wrong_encoded); + assert!(public_key_var_gen.is_err()); + + let secret_key_var_gen: Result = + serde_json::from_str(&wrong_encoded); + assert!(secret_key_var_gen.is_err()); + + let signature_var_gen: Result = + serde_json::from_str(&wrong_encoded); + assert!(signature_var_gen.is_err()); +} + +#[test] +fn too_long_encoded() { + let length_33_enc = "\"yaujE5CNg7SRYuf3Vw7G8QQdM7267QxJtfqGUEjLbxyCC\""; + let length_65_enc = "\"Hovyh2MvKLSnTfv2aKMMD1s7MgzWVCdzKJbbLwzU3kgVmo2JugxpGPASJWVQVXcxUqxtxVrQ63myzLRr1ko6oJvyv\""; + let length_97_enc = "\"7a5RpCdtr1aaXvaR3AofnEnVRh7kpzyqE8eYJpCBVLKLLpXVeN9UrXGRTZyq2upTVaJT5QnPQwZCGXW1oxrEAzrPvQ4vbWFwiHMJijZMzrPsTjQJFju1H4shrajuqUG4fYFpC\""; + + let public_key: Result = serde_json::from_str(&length_33_enc); + assert!(public_key.is_err()); + + let secret_key: Result = serde_json::from_str(&length_33_enc); + assert!(secret_key.is_err()); + + let signature: Result = serde_json::from_str(&length_65_enc); + assert!(signature.is_err()); + + let public_key_double: Result = + serde_json::from_str(&length_65_enc); + assert!(public_key_double.is_err()); + + let signature_double: Result = + serde_json::from_str(&length_97_enc); + assert!(signature_double.is_err()); + + let public_key_var_gen: Result = + serde_json::from_str(&length_65_enc); + assert!(public_key_var_gen.is_err()); + + let secret_key_var_gen: Result = + serde_json::from_str(&length_65_enc); + assert!(secret_key_var_gen.is_err()); + + let signature_var_gen: Result = + serde_json::from_str(&length_65_enc); + assert!(signature_var_gen.is_err()); +} + +#[test] +fn too_short_encoded() { + let length_31_enc = "\"3uTp29S3e2HQBekFYvVwsmoeEzk4uVWwQUjvJPwWKwU\""; + let length_63_enc = "\"YrHj6pQ3kRkpELFJK8a8ESdYyXaH9fQeb4pXRNEb8mSxDCrin1bF4uHz9BN13kN15mmH5fxXXSAusfLLGLrjCF\""; + let length_96_enc = "\"LZXkPWnz5xKxYnyDRZyJvL9vF44oQynzozqRBcpgWA3yZicbaxNeKKJrAMv3eXBbyEvk24mgz9Kg9tck5yEW6k16chN4hDWYUr5gDb9PJJ3YmUqcjG8yPaAuz3cNCE8dHv\""; + + let public_key: Result = serde_json::from_str(&length_31_enc); + assert!(public_key.is_err()); + + let secret_key: Result = serde_json::from_str(&length_31_enc); + assert!(secret_key.is_err()); + + let signature: Result = serde_json::from_str(&length_63_enc); + assert!(signature.is_err()); + + let public_key_double: Result = + serde_json::from_str(&length_63_enc); + assert!(public_key_double.is_err()); + + let signature_double: Result = + serde_json::from_str(&length_96_enc); + assert!(signature_double.is_err()); + + let public_key_var_gen: Result = + serde_json::from_str(&length_63_enc); + assert!(public_key_var_gen.is_err()); + + let secret_key_var_gen: Result = + serde_json::from_str(&length_63_enc); + assert!(secret_key_var_gen.is_err()); + + let signature_var_gen: Result = + serde_json::from_str(&length_63_enc); + assert!(signature_var_gen.is_err()); +}