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

Merge branch 'master' of github.com:narodnik/sapvi

rachel-rose 5 лет назад
Родитель
Сommit
f151347683

+ 1 - 0
Cargo.toml

@@ -28,6 +28,7 @@ sha2 = "0.9.1"
 rand_xorshift = "0.2"
 blake2s_simd = "0.5"
 blake2b_simd = "0.5.11"
+crypto_api_chachapoly = "0.4"
 bitvec = "0.18"
 bimap = "0.5.2"
 async-trait = "0.1.42"

+ 5 - 0
src/bin/spend-classic.rs

@@ -178,6 +178,7 @@ fn main() {
     let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let randomness_coin: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
     let secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
+    let signature_secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
 
     let merkle_path = [
         (bls12_381::Scalar::random(&mut OsRng), true),
@@ -192,6 +193,8 @@ fn main() {
     }
     let (params, pvk) = load_params("spend.params").expect("params should load");
 
+    let signature_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature_secret;
+
     let (proof, revealed) = create_spend_proof(
         &params,
         value,
@@ -200,7 +203,9 @@ fn main() {
         randomness_coin,
         secret,
         merkle_path,
+        signature_secret
     );
 
     assert!(verify_spend_proof(&pvk, &proof, &revealed));
+    assert_eq!(revealed.signature_public, signature_public);
 }

+ 19 - 1
src/bin/tx.rs

@@ -7,6 +7,7 @@ use rand::rngs::OsRng;
 use sapvi::crypto::{
     create_mint_proof, load_params, save_params, setup_mint_prover, verify_mint_proof,
     MintRevealedValues,
+    note::Note
 };
 
 struct TransactionBuilder {
@@ -126,7 +127,7 @@ struct TransactionOutput {
     revealed: MintRevealedValues,
 }
 
-fn main() {
+fn txbuilding() {
     {
         let params = setup_mint_prover();
         save_params("mint.params", &params);
@@ -143,3 +144,20 @@ fn main() {
     let tx = builder.build(&mint_params);
     assert!(tx.verify(&mint_pvk));
 }
+
+fn main() {
+    // txbuilding()
+    let note = Note {
+        serial: jubjub::Fr::random(&mut OsRng),
+        value: 110,
+        coin_blind: jubjub::Fr::random(&mut OsRng),
+        valcom_blind: jubjub::Fr::random(&mut OsRng),
+    };
+
+    let secret = jubjub::Fr::random(&mut OsRng);
+    let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
+
+    let encrypted_note = note.encrypt(&public).unwrap();
+    let note2 = encrypted_note.decrypt(&secret).unwrap();
+    assert_eq!(note.value, note2.value);
+}

+ 12 - 0
src/circuit/spend_contract.rs

@@ -27,6 +27,7 @@ pub struct SpendContract {
     pub is_right_2: Option<bool>,
     pub branch_3: Option<bls12_381::Scalar>,
     pub is_right_3: Option<bool>,
+    pub signature_secret: Option<jubjub::Fr>,
 }
 impl Circuit<bls12_381::Scalar> for SpendContract {
     fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
@@ -435,6 +436,17 @@ impl Circuit<bls12_381::Scalar> for SpendContract {
         // Line 253: emit_scalar current
         current.inputize(cs.namespace(|| "Line 253: emit_scalar current"))?;
 
+        let signature_secret = boolean::field_into_boolean_vec_le(
+            cs.namespace(|| "Signature secret"),
+            self.signature_secret,
+        )?;
+        let signature_public = ecc::fixed_base_multiplication(
+            cs.namespace(|| "Signature public"),
+            &zcash_proofs::constants::SPENDING_KEY_GENERATOR,
+            &signature_secret,
+        )?;
+        signature_public.inputize(cs.namespace(|| "Signature public inputize"))?;
+
         Ok(())
     }
 }

+ 5 - 1
src/crypto/diffie_hellman.rs

@@ -13,6 +13,10 @@ pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubju
     // <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
     // ExtendedPoint::mul_by_cofactor in the jubjub crate.
 
+    // ExtendedPoint::multiply currently just implements double-and-add,
+    // so using wNAF is a concrete speed improvement (as it operates over a window of bits
+    // instead of individual bits).
+    // We want that to be fast because it's in the hot path for trial decryption of notes on chain.
     let mut wnaf = group::Wnaf::new();
     wnaf.scalar(esk).base(*pk_d).clear_cofactor()
 }
@@ -20,7 +24,7 @@ pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubju
 /// Sapling KDF for note encryption.
 ///
 /// Implements section 5.4.4.4 of the Zcash Protocol Specification.
-fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::ExtendedPoint) -> Blake2bHash {
+pub fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::ExtendedPoint) -> Blake2bHash {
     Blake2bParams::new()
         .hash_length(32)
         .personal(KDF_SAPLING_PERSONALIZATION)

+ 25 - 0
src/crypto/fr_serial.rs

@@ -0,0 +1,25 @@
+use std::io;
+use crate::serial::{Encodable, Decodable, ReadExt, WriteExt};
+
+use crate::error::{Error, Result};
+
+impl Encodable for jubjub::Fr {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        s.write_slice(&self.to_bytes()[..])?;
+        Ok(32)
+    }
+}
+
+impl Decodable for jubjub::Fr {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let mut bytes = [0u8; 32];
+        d.read_slice(&mut bytes)?;
+        let result = Self::from_bytes(&bytes);
+        if result.is_some().into() {
+            Ok(result.unwrap())
+        } else {
+            Err(Error::BadOperationType)
+        }
+    }
+}
+

+ 2 - 0
src/crypto/mod.rs

@@ -1,5 +1,7 @@
 pub mod diffie_hellman;
+pub mod fr_serial;
 pub mod mint_proof;
+pub mod note;
 pub mod schnorr;
 pub mod spend_proof;
 pub mod util;

+ 116 - 0
src/crypto/note.rs

@@ -0,0 +1,116 @@
+use crypto_api_chachapoly::ChachaPolyIetf;
+use ff::Field;
+use std::io;
+use rand::rngs::OsRng;
+
+use crate::serial::{Encodable, Decodable, ReadExt, WriteExt};
+use crate::error::{Error, Result};
+use super::diffie_hellman::{sapling_ka_agree, kdf_sapling};
+
+pub const NOTE_PLAINTEXT_SIZE: usize =
+    32 + // serial
+    8 + // value
+    32 + // coin_blind
+    32; // valcom_blind
+pub const AEAD_TAG_SIZE: usize = 16;
+pub const ENC_CIPHERTEXT_SIZE: usize = NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE;
+
+pub struct Note {
+    pub serial: jubjub::Fr,
+    pub value: u64,
+    pub coin_blind: jubjub::Fr,
+    pub valcom_blind: jubjub::Fr,
+}
+
+impl Encodable for Note {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.serial.encode(&mut s)?;
+        len += self.value.encode(&mut s)?;
+        len += self.coin_blind.encode(&mut s)?;
+        len += self.valcom_blind.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for Note {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            serial: Decodable::decode(&mut d)?,
+            value: Decodable::decode(&mut d)?,
+            coin_blind: Decodable::decode(&mut d)?,
+            valcom_blind: Decodable::decode(d)?
+        })
+    }
+}
+
+impl Note {
+    pub fn encrypt(&self, public: &jubjub::SubgroupPoint) -> Result<EncryptedNote> {
+        let ephem_secret = jubjub::Fr::random(&mut OsRng);
+        let ephem_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * ephem_secret;
+        let shared_secret = sapling_ka_agree(&ephem_secret, public.into());
+        let key = kdf_sapling(shared_secret, &ephem_public.into());
+
+        let mut input = Vec::new();
+        self.encode(&mut input)?;
+
+        let mut ciphertext = [0u8; ENC_CIPHERTEXT_SIZE];
+        assert_eq!(
+            ChachaPolyIetf::aead_cipher()
+                .seal_to(&mut ciphertext, &input, &[], key.as_ref(), &[0u8; 12])
+                .unwrap(),
+            ENC_CIPHERTEXT_SIZE
+        );
+
+        Ok(EncryptedNote {
+            ciphertext,
+            ephem_public
+        })
+    }
+}
+
+pub struct EncryptedNote {
+    ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
+    ephem_public: jubjub::SubgroupPoint
+}
+
+impl EncryptedNote {
+    pub fn decrypt(&self, secret: &jubjub::Fr) -> Result<Note> {
+        let shared_secret = sapling_ka_agree(&secret, &self.ephem_public.into());
+        let key = kdf_sapling(shared_secret, &self.ephem_public.into());
+
+        let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
+        assert_eq!(
+            ChachaPolyIetf::aead_cipher()
+                .open_to(
+                    &mut plaintext,
+                    &self.ciphertext,
+                    &[],
+                    key.as_ref(),
+                    &[0u8; 12]
+                )
+                .map_err(|_| Error::NoteDecryptionFailed)?,
+            NOTE_PLAINTEXT_SIZE
+        );
+
+        Note::decode(&plaintext[..])
+    }
+}
+
+#[test]
+fn test_note_encdec() {
+    let note = Note {
+        serial: jubjub::Fr::random(&mut OsRng),
+        value: 110,
+        coin_blind: jubjub::Fr::random(&mut OsRng),
+        valcom_blind: jubjub::Fr::random(&mut OsRng),
+    };
+
+    let secret = jubjub::Fr::random(&mut OsRng);
+    let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
+
+    let encrypted_note = note.encrypt(&public).unwrap();
+    let note2 = encrypted_note.decrypt(&secret).unwrap();
+    assert_eq!(note.value, note2.value);
+}
+

+ 21 - 2
src/crypto/spend_proof.rs

@@ -54,6 +54,7 @@ pub struct SpendRevealedValues {
     // This should not be here, we just have it for debugging
     //coin: [u8; 32],
     pub merkle_root: bls12_381::Scalar,
+    pub signature_public: jubjub::SubgroupPoint
 }
 
 impl SpendRevealedValues {
@@ -64,6 +65,7 @@ impl SpendRevealedValues {
         randomness_coin: &jubjub::Fr,
         secret: &jubjub::Fr,
         merkle_path: &[(bls12_381::Scalar, bool)],
+        signature_secret: &jubjub::Fr,
     ) -> Self {
         let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR
             * jubjub::Fr::from(value))
@@ -83,6 +85,7 @@ impl SpendRevealedValues {
         );
 
         let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
+        let signature_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * signature_secret;
 
         let mut coin = [0; 32];
         coin.copy_from_slice(
@@ -118,11 +121,12 @@ impl SpendRevealedValues {
             value_commit,
             nullifier,
             merkle_root,
+            signature_public
         }
     }
 
-    fn make_outputs(&self) -> [bls12_381::Scalar; 5] {
-        let mut public_input = [bls12_381::Scalar::zero(); 5];
+    fn make_outputs(&self) -> [bls12_381::Scalar; 7] {
+        let mut public_input = [bls12_381::Scalar::zero(); 7];
 
         // CV
         {
@@ -164,6 +168,16 @@ impl SpendRevealedValues {
 
         public_input[4] = self.merkle_root;
 
+        {
+            let result = jubjub::ExtendedPoint::from(self.signature_public);
+            let affine = result.to_affine();
+            //let (u, v) = (affine.get_u(), affine.get_v());
+            let u = affine.get_u();
+            let v = affine.get_v();
+            public_input[5] = u;
+            public_input[6] = v;
+        }
+
         public_input
     }
 }
@@ -187,6 +201,8 @@ pub fn setup_spend_prover() -> groth16::Parameters<Bls12> {
             is_right_2: None,
             branch_3: None,
             is_right_3: None,
+
+            signature_secret: None,
         };
         groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
     };
@@ -202,6 +218,7 @@ pub fn create_spend_proof(
     randomness_coin: jubjub::Fr,
     secret: jubjub::Fr,
     merkle_path: [(bls12_381::Scalar, bool); 4],
+    signature_secret: jubjub::Fr,
 ) -> (groth16::Proof<Bls12>, SpendRevealedValues) {
     let c = SpendContract {
         value: Some(value),
@@ -218,6 +235,7 @@ pub fn create_spend_proof(
         is_right_2: Some(merkle_path[2].1),
         branch_3: Some(merkle_path[3].0),
         is_right_3: Some(merkle_path[3].1),
+        signature_secret: Some(signature_secret),
     };
 
     let start = Instant::now();
@@ -231,6 +249,7 @@ pub fn create_spend_proof(
         &randomness_coin,
         &secret,
         &merkle_path,
+        &signature_secret
     );
 
     (proof, revealed)

+ 2 - 0
src/error.rs

@@ -40,6 +40,7 @@ pub enum Error {
     ChannelTimeout,
     ServiceStopped,
     Utf8Error,
+    NoteDecryptionFailed,
 }
 
 impl std::error::Error for Error {}
@@ -82,6 +83,7 @@ impl fmt::Display for Error {
             Error::ChannelTimeout => f.write_str("Channel timed out"),
             Error::ServiceStopped => f.write_str("Service stopped"),
             Error::Utf8Error => f.write_str("Malformed UTF8"),
+            Error::NoteDecryptionFailed => f.write_str("Unable to decrypt mint note"),
         }
     }
 }