Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ blake2b_simd = "1"
ff = "0.12"
fpe = "0.5"
group = { version = "0.12.1", features = ["wnaf-memuse"] }
halo2_gadgets = "0.2"
halo2_proofs = "0.2"
halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "append_sinsemilla_commit" }
halo2_proofs = { git = "https://github.com/QED-it/halo2", branch = "append_sinsemilla_commit" }
hex = "0.4"
lazy_static = "1"
memuse = { version = "0.2.1", features = ["nonempty"] }
Expand All @@ -52,7 +52,7 @@ plotters = { version = "0.3.0", optional = true }

[dev-dependencies]
criterion = "0.3"
halo2_gadgets = { version = "0.2", features = ["test-dependencies"] }
halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "append_sinsemilla_commit", features = ["test-dependencies"] }
hex = "0.4"
proptest = "1.0.0"
zcash_note_encryption = { version = "0.2", features = ["pre-zip-212"] }
Expand Down
156 changes: 136 additions & 20 deletions src/note/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ use core::iter;
use bitvec::{array::BitArray, order::Lsb0};
use group::ff::{PrimeField, PrimeFieldBits};
use halo2_gadgets::sinsemilla::primitives as sinsemilla;
use halo2_gadgets::sinsemilla::primitives::append_hash_to_point;
use pasta_curves::pallas;
use subtle::{ConstantTimeEq, CtOption};

use crate::{
constants::{
fixed_bases::{NOTE_COMMITMENT_PERSONALIZATION, NOTE_ZSA_COMMITMENT_PERSONALIZATION},
sinsemilla::K,
L_ORCHARD_BASE,
},
note::asset_base::AssetBase,
Expand Down Expand Up @@ -56,35 +58,70 @@ impl NoteCommitment {
let rho_bits = rho.to_le_bits();
let psi_bits = psi.to_le_bits();

let zec_note_bits = iter::empty()
let remaining_bits =
(g_d_bits.len() + pk_d_bits.len() + v_bits.len() + 2 * L_ORCHARD_BASE) % K;

let (psi_bits_left, psi_bits_right) = psi_bits.split_at(L_ORCHARD_BASE - remaining_bits);

let common_bits = iter::empty()
.chain(g_d_bits.iter().by_vals())
.chain(pk_d_bits.iter().by_vals())
.chain(v_bits.iter().by_vals())
.chain(rho_bits.iter().by_vals().take(L_ORCHARD_BASE))
.chain(psi_bits.iter().by_vals().take(L_ORCHARD_BASE));
.chain(psi_bits_left.iter().by_vals());

// TODO: make this constant-time.
if asset.is_native().into() {
// Commit to ZEC notes as per the Orchard protocol.
Self::commit(NOTE_COMMITMENT_PERSONALIZATION, zec_note_bits, rcm)
} else {
// Commit to non-ZEC notes as per the ZSA protocol.
// Append the note type to the Orchard note encoding.
let type_bits = BitArray::<_, Lsb0>::new(asset.to_bytes());
let zsa_note_bits = zec_note_bits.chain(type_bits.iter().by_vals());

// Commit in a different domain than Orchard notes.
Self::commit(NOTE_ZSA_COMMITMENT_PERSONALIZATION, zsa_note_bits, rcm)
}
let zec_suffix = psi_bits_right.iter().by_vals().take(remaining_bits);
let type_bits = BitArray::<_, Lsb0>::new(asset.to_bytes());
let zsa_suffix = iter::empty()
.chain(psi_bits_right.iter().by_vals().take(remaining_bits))
.chain(type_bits.iter().by_vals());

Self::double_constant_time_commit(
NOTE_COMMITMENT_PERSONALIZATION,
NOTE_ZSA_COMMITMENT_PERSONALIZATION,
common_bits,
zec_suffix,
zsa_suffix,
rcm,
asset.is_native().into(),
)
}

fn commit(
personalization: &str,
bits: impl Iterator<Item = bool>,
/// Evaluates `SinsemillaCommit_{rcm}(personalization1, common_bits||suffix1)` and
/// `SinsemillaCommit_{rcm}(personalization2, common_bits||suffix2)` and returns the commit
/// corresponding to the boolean `choice`.
///
/// We would like to have a constant time implementation even if suffix1 and suffix2 have not
/// the same length.
/// `common_bits` must be a multiple of K bits
fn double_constant_time_commit(
personalization1: &str,
personalization2: &str,
common_bits: impl Iterator<Item = bool>,
suffix1: impl Iterator<Item = bool>,
suffix2: impl Iterator<Item = bool>,
rcm: NoteCommitTrapdoor,
choice: bool,
) -> CtOption<Self> {
let domain = sinsemilla::CommitDomain::new(personalization);
domain.commit(bits, &rcm.0).map(NoteCommitment)
// Select the desired personalization
let domain = if choice {
sinsemilla::CommitDomain::new(personalization1)
} else {
sinsemilla::CommitDomain::new(personalization2)
};
// Evaluate the hash on the `common_bits`
let common_hash = domain.hash_to_point_inner(common_bits);
// Continue to evaluate the hash from the previous hash with each possible suffix
// We would like to have a constant time implementation. Hence, we have to evaluate the
// hash for the both suffixes
let hash1 = append_hash_to_point(common_hash, suffix1);
let hash2 = append_hash_to_point(common_hash, suffix2);
// Select the desired hash
let note_hash = if choice { hash1 } else { hash2 };
// Evaluate the commitment from this hash point
domain
.commit_from_hash_point(note_hash, &rcm.0)
.map(NoteCommitment)
}
}

Expand Down Expand Up @@ -140,3 +177,82 @@ impl PartialEq for ExtractedNoteCommitment {
}

impl Eq for ExtractedNoteCommitment {}

#[cfg(test)]
mod tests {
use crate::constants::fixed_bases::{
NOTE_COMMITMENT_PERSONALIZATION, NOTE_ZSA_COMMITMENT_PERSONALIZATION,
};
use crate::note::commitment::NoteCommitTrapdoor;
use crate::note::NoteCommitment;
use ff::Field;
use halo2_gadgets::sinsemilla::primitives as sinsemilla;
use pasta_curves::pallas;
use rand::{rngs::OsRng, Rng};

#[test]
fn test_double_constant_time_commit() {
let mut os_rng = OsRng::default();
let prefix: Vec<bool> = (0..30).map(|_| os_rng.gen::<bool>()).collect();
let suffix_zec: Vec<bool> = (0..6).map(|_| os_rng.gen::<bool>()).collect();
let suffix_zsa: Vec<bool> = (0..25).map(|_| os_rng.gen::<bool>()).collect();

let rcm = NoteCommitTrapdoor(pallas::Scalar::random(&mut os_rng));

let commit_zec = {
let domain = sinsemilla::CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
domain
.commit(
prefix
.clone()
.into_iter()
.chain(suffix_zec.clone().into_iter()),
&rcm.0,
)
.unwrap()
};

assert_eq!(
commit_zec,
NoteCommitment::double_constant_time_commit(
NOTE_COMMITMENT_PERSONALIZATION,
NOTE_ZSA_COMMITMENT_PERSONALIZATION,
prefix.clone().into_iter(),
suffix_zec.clone().into_iter(),
suffix_zsa.clone().into_iter(),
rcm.clone(),
true
)
.unwrap()
.0
);

let commit_zsa = {
let domain = sinsemilla::CommitDomain::new(NOTE_ZSA_COMMITMENT_PERSONALIZATION);
domain
.commit(
prefix
.clone()
.into_iter()
.chain(suffix_zsa.clone().into_iter()),
&rcm.0,
)
.unwrap()
};

assert_eq!(
commit_zsa,
NoteCommitment::double_constant_time_commit(
NOTE_COMMITMENT_PERSONALIZATION,
NOTE_ZSA_COMMITMENT_PERSONALIZATION,
prefix.into_iter(),
suffix_zec.into_iter(),
suffix_zsa.into_iter(),
rcm,
false
)
.unwrap()
.0
);
}
}