Implement Sinsemilla primitives

This commit is contained in:
Jack Grigg 2021-02-24 23:04:43 +00:00 committed by therealyingtong
parent 4040aba96a
commit a03ee8797d
3 changed files with 99 additions and 0 deletions

View File

@ -19,6 +19,7 @@ publish = false
rustdoc-args = [ "--html-in-header", "katex-header.html" ]
[dependencies]
group = "0.9"
halo2 = { git = "https://github.com/zcash/halo2.git", branch = "main" }
nonempty = "0.6"

View File

@ -5,3 +5,4 @@
// - EphemeralSecretKey
pub mod redpallas;
pub(crate) mod sinsemilla;

View File

@ -0,0 +1,97 @@
//! The Sinsemilla hash function and commitment scheme.
use std::iter;
use group::{Curve, Group};
use halo2::{
arithmetic::{CurveAffine, CurveExt},
pasta::pallas,
};
const GROUP_HASH_Q: &str = "z.cash:SinsemillaQ";
const GROUP_HASH_S: &str = "z.cash:SinsemillaS";
const K: usize = 10;
const C: usize = 253;
fn lebs2ip_32(bits: &[bool]) -> u32 {
bits.iter()
.enumerate()
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
}
/// Hash extractor for Pallas, from [§ 5.4.8.7].
///
/// [§ 5.4.8.7]: https://zips.z.cash/protocol/orchard.pdf#concreteextractorpallas
fn extract(point: &pallas::Point) -> pallas::Base {
// TODO: Should we return the actual bits in a Vec, or allow the caller to use
// PrimeField::to_le_bits on the returned pallas::Base?
if let Some((x, _)) = point.to_affine().get_xy().into() {
x
} else {
pallas::Base::zero()
}
}
#[allow(non_snake_case)]
fn Q(domain_prefix: &str) -> pallas::Point {
pallas::Point::hash_to_curve(GROUP_HASH_Q)(domain_prefix.as_bytes())
}
/// `SinsemillaHashToPoint` from [§ 5.4.1.9].
///
/// [§ 5.4.1.9]: https://zips.z.cash/protocol/orchard.pdf#concretesinsemillahash
#[allow(non_snake_case)]
pub(crate) fn hash_to_point(
domain_prefix: &str,
msg: impl Iterator<Item = bool> + ExactSizeIterator,
) -> pallas::Point {
assert!(msg.len() <= K * C);
let pad = msg.len() % K;
let padded: Vec<_> = msg.chain(iter::repeat(false).take(pad)).collect();
let hasher_S = pallas::Point::hash_to_curve(GROUP_HASH_S);
let S = |chunk: &[bool]| hasher_S(&lebs2ip_32(chunk).to_le_bytes());
padded
.chunks(K)
.fold(Q(domain_prefix), |acc, chunk| acc.double() + S(chunk))
}
/// `SinsemillaHash` from [§ 5.4.1.9].
///
/// [§ 5.4.1.9]: https://zips.z.cash/protocol/orchard.pdf#concretesinsemillahash
pub(crate) fn hash(
domain_prefix: &str,
msg: impl Iterator<Item = bool> + ExactSizeIterator,
) -> pallas::Base {
extract(&hash_to_point(domain_prefix, msg))
}
/// `SinsemillaCommit` from [§ 5.4.7.4].
///
/// [§ 5.4.7.4]: https://zips.z.cash/protocol/orchard.pdf#concretesinsemillacommit
#[allow(non_snake_case)]
pub(crate) fn commit(
domain_prefix: &str,
msg: impl Iterator<Item = bool> + ExactSizeIterator,
r: &pallas::Scalar,
) -> pallas::Point {
let m_prefix = domain_prefix.to_owned() + "-M";
let r_prefix = domain_prefix.to_owned() + "-r";
let hasher_r = pallas::Point::hash_to_curve(&r_prefix);
hash_to_point(&m_prefix, msg) + hasher_r(&[]) * r
}
/// `SinsemillaShortCommit` from [§ 5.4.7.4].
///
/// [§ 5.4.7.4]: https://zips.z.cash/protocol/orchard.pdf#concretesinsemillacommit
pub(crate) fn short_commit(
domain_prefix: &str,
msg: impl Iterator<Item = bool> + ExactSizeIterator,
r: &pallas::Scalar,
) -> pallas::Base {
extract(&commit(domain_prefix, msg, r))
}