Просмотр исходного кода

By default, when serializing a CMZCredential, skip the private key

That way, you don't accidentally send the private key if the issuer
creates a credential completely on its own (not via a typical credential
issuing protocol) and sends it to the client.

Also add serialize_with_privkey and deserialize_with_privkey if you
explicitly _do_ want to keep the private key (for example, if the issuer
is saving a credential to its own storage).

Thanks to Morgan Hill from Radically Open Security.
Ian Goldberg 1 месяц назад
Родитель
Сommit
a9cd473a53
3 измененных файлов с 92 добавлено и 2 удалено
  1. 1 1
      Cargo.toml
  2. 20 1
      src/lib.rs
  3. 71 0
      tests/priv_serialize.rs

+ 1 - 1
Cargo.toml

@@ -7,6 +7,7 @@ repository = "https://git-crysp.uwaterloo.ca/SigmaProtocol/cmz"
 description = "A crate to automatically create protocols that use CMZ14 or µCMZ credentials, by specifying an extremely compact description of the protocol."
 
 [dependencies]
+bincode = "1"
 cmz-derive = "=0.2.1"
 ff = "0.13"
 generic_static = "0.2"
@@ -21,7 +22,6 @@ sigma-compiler = "0.2.2"
 thiserror = "2"
 
 [dev-dependencies]
-bincode = "1"
 chrono = "0.4"
 curve25519-dalek = { version = "4", features = [ "group", "rand_core", "digest" ] }
 sha2 = "0.10"

+ 20 - 1
src/lib.rs

@@ -308,7 +308,7 @@ pub fn cmz_privkey_to_pubkey<G: PrimeGroup>(privkey: &CMZPrivkey<G>) -> CMZPubke
 /// The CMZCredential trait implemented by all CMZ credential struct types.
 pub trait CMZCredential
 where
-    Self: Default + Sized,
+    for<'a> Self: Default + Sized + serde::Serialize + serde::Deserialize<'a>,
 {
     /// The type of attributes for this credential
     type Scalar: PrimeField;
@@ -420,6 +420,21 @@ where
     /// example, when you're doing an OR proof, and in some arms of the
     /// disjunction, the credential does not have to be valid.
     fn fake_MAC(&mut self, rng: &mut impl RngCore);
+
+    /// Serialize this credential, _including_ the private key.  The
+    /// default serializer skips the private key for safety reasons.
+    fn serialize_with_privkey(&self) -> Vec<u8> {
+        // Accomplish this by serializing the pair (privkey, self)
+        bincode::serialize(&(self.get_privkey(), self)).unwrap()
+    }
+
+    /// Deserialize this credential, _including_ the private key.  The
+    /// default serializer skips the private key for safety reasons.
+    fn deserialize_with_privkey(bytes: &[u8]) -> bincode::Result<Self> {
+        let (privkey, mut cred) = bincode::deserialize::<(CMZPrivkey<Self::Point>, Self)>(bytes)?;
+        cred.set_privkey(&privkey);
+        Ok(cred)
+    }
 }
 
 /// The CMZ macro for declaring CMZ credentials.
@@ -462,6 +477,8 @@ macro_rules! CMZ {
             pub $id: Option<<$G as Group>::Scalar>,
         )+
             pub MAC: CMZMac<$G>,
+            // Don't serialize the private key by default
+            #[serde(skip)]
             privkey: CMZPrivkey<$G>,
             pubkey: CMZPubkey<$G>,
         }
@@ -476,6 +493,8 @@ macro_rules! CMZ {
             pub $id: Option<<G as Group>::Scalar>,
         )+
             pub MAC: CMZMac<G>,
+            // Don't serialize the private key by default
+            #[serde(skip)]
             privkey: CMZPrivkey<G>,
             pubkey: CMZPubkey<G>,
         }

+ 71 - 0
tests/priv_serialize.rs

@@ -0,0 +1,71 @@
+use cmz::*;
+use curve25519_dalek::ristretto::RistrettoPoint;
+use curve25519_dalek::scalar::Scalar;
+use group::Group;
+use rand::{CryptoRng, RngCore};
+use sha2::Sha512;
+
+CMZ! { Basic<RistrettoPoint> :
+    attr1,
+    attr2
+}
+
+CMZ14Protocol! { basic_proto,
+A: Basic {
+    attr1: H,
+    attr2: H,
+}, , }
+
+// Test that the default serialization of a credential does _not_
+// include any private key material.  Thanks to Morgan Hill from
+// Radically Open Security.
+#[test]
+fn test_default_serialize() {
+    let mut rng = rand::thread_rng();
+    cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
+        b"CMZ Generator A",
+    ));
+
+    let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
+
+    let mut basic_cred = Basic::using_privkey(&privkey);
+    basic_cred.attr1 = Some(Scalar::ZERO);
+    basic_cred.attr2 = Some(Scalar::ONE);
+    basic_cred.create_MAC(&mut rng, &privkey).unwrap();
+
+    let basic_cred_bytes = bincode::serialize(&basic_cred).unwrap();
+
+    let basic_cred_deser = bincode::deserialize::<Basic>(&basic_cred_bytes).unwrap();
+
+    assert_eq!(
+        *basic_cred_deser.get_privkey(),
+        CMZPrivkey::<RistrettoPoint>::default()
+    );
+
+    assert_eq!(*basic_cred_deser.get_pubkey(), pubkey);
+}
+
+// Test that the serialization with privkey of a credential _does_
+// include the private key material.
+#[test]
+fn test_priv_serialize() {
+    let mut rng = rand::thread_rng();
+    cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
+        b"CMZ Generator A",
+    ));
+
+    let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
+
+    let mut basic_cred = Basic::using_privkey(&privkey);
+    basic_cred.attr1 = Some(Scalar::ZERO);
+    basic_cred.attr2 = Some(Scalar::ONE);
+    basic_cred.create_MAC(&mut rng, &privkey).unwrap();
+
+    let basic_cred_bytes = basic_cred.serialize_with_privkey();
+
+    let basic_cred_deser = Basic::deserialize_with_privkey(&basic_cred_bytes).unwrap();
+
+    assert_eq!(*basic_cred_deser.get_privkey(), privkey);
+
+    assert_eq!(*basic_cred_deser.get_pubkey(), pubkey);
+}