mirror of https://github.com/zcash/halo2.git
Merge pull request #40 from zcash/key-components
Orchard key components
This commit is contained in:
commit
e737b50a25
10
Cargo.toml
10
Cargo.toml
|
@ -19,9 +19,19 @@ publish = false
|
|||
rustdoc-args = [ "--html-in-header", "katex-header.html" ]
|
||||
|
||||
[dependencies]
|
||||
aes = "0.6"
|
||||
bitvec = "0.20"
|
||||
blake2b_simd = "0.5"
|
||||
ff = "0.9"
|
||||
fpe = "0.4"
|
||||
group = "0.9"
|
||||
halo2 = { git = "https://github.com/zcash/halo2.git", branch = "main" }
|
||||
nonempty = "0.6"
|
||||
subtle = "2.3"
|
||||
|
||||
[dependencies.reddsa]
|
||||
git = "https://github.com/str4d/redjubjub.git"
|
||||
rev = "f8ff124a52d86e122e0705e8e9272f2099fe4c46"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
use crate::keys::Diversifier;
|
||||
use crate::keys::{DiversifiedTransmissionKey, Diversifier};
|
||||
|
||||
/// A shielded payment address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use orchard::keys::{SpendingKey, FullViewingKey};
|
||||
///
|
||||
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
||||
/// let address = FullViewingKey::from(&sk).default_address();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Address {
|
||||
d: Diversifier,
|
||||
pk_d: (),
|
||||
pk_d: DiversifiedTransmissionKey,
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub(crate) fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self {
|
||||
Address { d, pk_d }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
//! Constants used in the Orchard protocol.
|
||||
|
||||
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
|
||||
pub(crate) const L_ORCHARD_BASE: usize = 255;
|
246
src/keys.rs
246
src/keys.rs
|
@ -1,41 +1,117 @@
|
|||
//! Key structures for Orchard.
|
||||
|
||||
use crate::address::Address;
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
|
||||
use aes::Aes256;
|
||||
use fpe::ff1::{BinaryNumeralString, FF1};
|
||||
use group::GroupEncoding;
|
||||
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
||||
use subtle::CtOption;
|
||||
|
||||
use crate::{
|
||||
address::Address,
|
||||
primitives::redpallas::{self, SpendAuth},
|
||||
spec::{
|
||||
commit_ivk, diversify_hash, extract_p, ka_orchard, prf_expand, prf_expand_vec, to_base,
|
||||
to_scalar,
|
||||
},
|
||||
};
|
||||
|
||||
/// A spending key, from which all key material is derived.
|
||||
///
|
||||
/// TODO: In Sapling we never actually used this, instead deriving everything via ZIP 32,
|
||||
/// so that we could maintain Bitcoin-like HD keys with properties like non-hardened
|
||||
/// derivation. If we decide that we don't actually require non-hardened derivation, then
|
||||
/// we could greatly simplify the HD structure and use this struct directly.
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub struct SpendingKey;
|
||||
pub struct SpendingKey([u8; 32]);
|
||||
|
||||
impl SpendingKey {
|
||||
/// Constructs an Orchard spending key from uniformly-random bytes.
|
||||
///
|
||||
/// Returns `None` if the bytes do not correspond to a valid Orchard spending key.
|
||||
pub fn from_bytes(sk: [u8; 32]) -> CtOption<Self> {
|
||||
let sk = SpendingKey(sk);
|
||||
// If ask = 0, discard this key.
|
||||
let ask = SpendAuthorizingKey::derive_inner(&sk);
|
||||
CtOption::new(sk, !ask.ct_is_zero())
|
||||
}
|
||||
}
|
||||
|
||||
/// A spend authorizing key, used to create spend authorization signatures.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SpendAuthorizingKey;
|
||||
pub(crate) struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
|
||||
|
||||
impl SpendAuthorizingKey {
|
||||
/// Derives ask from sk. Internal use only, does not enforce all constraints.
|
||||
fn derive_inner(sk: &SpendingKey) -> pallas::Scalar {
|
||||
to_scalar(prf_expand(&sk.0, &[0x06]))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SpendingKey> for SpendAuthorizingKey {
|
||||
fn from(_: &SpendingKey) -> Self {
|
||||
todo!()
|
||||
fn from(sk: &SpendingKey) -> Self {
|
||||
let ask = Self::derive_inner(sk);
|
||||
// SpendingKey cannot be constructed such that this assertion would fail.
|
||||
assert!(!bool::from(ask.ct_is_zero()));
|
||||
// TODO: Add TryFrom<S::Scalar> for SpendAuthorizingKey.
|
||||
let ret = SpendAuthorizingKey(ask.to_bytes().try_into().unwrap());
|
||||
// If the last bit of repr_P(ak) is 1, negate ask.
|
||||
if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 {
|
||||
SpendAuthorizingKey((-ask).to_bytes().try_into().unwrap())
|
||||
} else {
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: This is its protocol spec name for Sapling, but I'd prefer a different name.
|
||||
/// A key used to validate spend authorization signatures.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
/// Note that this is $\mathsf{ak}^\mathbb{P}$, which by construction is equivalent to
|
||||
/// $\mathsf{ak}$ but stored here as a RedPallas verification key.
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AuthorizingKey;
|
||||
pub(crate) struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
||||
|
||||
impl From<&SpendAuthorizingKey> for AuthorizingKey {
|
||||
fn from(_: &SpendAuthorizingKey) -> Self {
|
||||
todo!()
|
||||
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
||||
fn from(ask: &SpendAuthorizingKey) -> Self {
|
||||
SpendValidatingKey((&ask.0).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A key used to derive [`Nullifier`]s from [`Note`]s.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [`Nullifier`]: crate::note::Nullifier
|
||||
/// [`Note`]: crate::note::Note
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NullifierDerivingKey;
|
||||
pub(crate) struct NullifierDerivingKey(pallas::Base);
|
||||
|
||||
impl From<&SpendingKey> for NullifierDerivingKey {
|
||||
fn from(_: &SpendingKey) -> Self {
|
||||
todo!()
|
||||
fn from(sk: &SpendingKey) -> Self {
|
||||
NullifierDerivingKey(to_base(prf_expand(&sk.0, &[0x07])))
|
||||
}
|
||||
}
|
||||
|
||||
/// The randomness for $\mathsf{Commit}^\mathsf{ivk}$.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
struct CommitIvkRandomness(pallas::Scalar);
|
||||
|
||||
impl From<&SpendingKey> for CommitIvkRandomness {
|
||||
fn from(sk: &SpendingKey) -> Self {
|
||||
CommitIvkRandomness(to_scalar(prf_expand(&sk.0, &[0x08])))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,29 +120,111 @@ impl From<&SpendingKey> for NullifierDerivingKey {
|
|||
/// This key is useful anywhere you need to maintain accurate balance, but do not want the
|
||||
/// ability to spend funds (such as a view-only wallet).
|
||||
///
|
||||
/// TODO: Should we just define the FVK to include extended stuff like the diversifier key?
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub struct FullViewingKey {
|
||||
ak: AuthorizingKey,
|
||||
ak: SpendValidatingKey,
|
||||
nk: NullifierDerivingKey,
|
||||
rivk: (),
|
||||
rivk: CommitIvkRandomness,
|
||||
}
|
||||
|
||||
impl From<&SpendingKey> for FullViewingKey {
|
||||
fn from(_: &SpendingKey) -> Self {
|
||||
todo!()
|
||||
fn from(sk: &SpendingKey) -> Self {
|
||||
FullViewingKey {
|
||||
ak: (&SpendAuthorizingKey::from(sk)).into(),
|
||||
nk: sk.into(),
|
||||
rivk: sk.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FullViewingKey {
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
|
||||
let k = self.rivk.0.to_bytes();
|
||||
let b = [(&self.ak.0).into(), self.nk.0.to_bytes()];
|
||||
let r = prf_expand_vec(&k, &[&[0x82], &b[0][..], &b[1][..]]);
|
||||
(
|
||||
DiversifierKey(r[..32].try_into().unwrap()),
|
||||
OutgoingViewingKey(r[32..].try_into().unwrap()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the default payment address for this key.
|
||||
pub fn default_address(&self) -> Address {
|
||||
self.address(DiversifierKey::from(self).default_diversifier())
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key at the given index.
|
||||
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
|
||||
self.address(DiversifierKey::from(self).get(j))
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||
pub fn address(&self, d: Diversifier) -> Address {
|
||||
IncomingViewingKey::from(self).address(d)
|
||||
}
|
||||
}
|
||||
|
||||
/// A key that provides the capability to derive a sequence of diversifiers.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub struct DiversifierKey([u8; 32]);
|
||||
|
||||
impl From<&FullViewingKey> for DiversifierKey {
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
fvk.derive_dk_ovk().0
|
||||
}
|
||||
}
|
||||
|
||||
/// The index for a particular diversifier.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct DiversifierIndex([u8; 11]);
|
||||
|
||||
macro_rules! di_from {
|
||||
($n:ident) => {
|
||||
impl From<$n> for DiversifierIndex {
|
||||
fn from(j: $n) -> Self {
|
||||
let mut j_bytes = [0; 11];
|
||||
j_bytes[..mem::size_of::<$n>()].copy_from_slice(&j.to_le_bytes());
|
||||
DiversifierIndex(j_bytes)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
di_from!(u32);
|
||||
di_from!(u64);
|
||||
di_from!(usize);
|
||||
|
||||
impl DiversifierKey {
|
||||
/// Returns the diversifier at index 0.
|
||||
pub fn default_diversifier(&self) -> Diversifier {
|
||||
self.get(0u32)
|
||||
}
|
||||
|
||||
/// Returns the diversifier at the given index.
|
||||
pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
|
||||
let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
|
||||
let enc = ff
|
||||
.encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.into().0[..]))
|
||||
.unwrap();
|
||||
Diversifier(enc.to_bytes_le().try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// A diversifier that can be used to derive a specific [`Address`] from a
|
||||
/// [`FullViewingKey`] or [`IncomingViewingKey`].
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub struct Diversifier([u8; 11]);
|
||||
|
||||
|
@ -78,19 +236,25 @@ pub struct Diversifier([u8; 11]);
|
|||
///
|
||||
/// This key is not suitable for use on its own in a wallet, as it cannot maintain
|
||||
/// accurate balance. You should use a [`FullViewingKey`] instead.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub struct IncomingViewingKey;
|
||||
pub struct IncomingViewingKey(pallas::Scalar);
|
||||
|
||||
impl From<&FullViewingKey> for IncomingViewingKey {
|
||||
fn from(_: &FullViewingKey) -> Self {
|
||||
todo!()
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
|
||||
IncomingViewingKey(commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl IncomingViewingKey {
|
||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||
pub fn address(&self, _: Diversifier) -> Address {
|
||||
todo!()
|
||||
pub fn address(&self, d: Diversifier) -> Address {
|
||||
let pk_d = DiversifiedTransmissionKey::derive(self, &d);
|
||||
Address::from_parts(d, pk_d)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,11 +263,33 @@ impl IncomingViewingKey {
|
|||
///
|
||||
/// This key is not suitable for use on its own in a wallet, as it cannot maintain
|
||||
/// accurate balance. You should use a [`FullViewingKey`] instead.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub struct OutgoingViewingKey;
|
||||
pub struct OutgoingViewingKey([u8; 32]);
|
||||
|
||||
impl From<&FullViewingKey> for OutgoingViewingKey {
|
||||
fn from(_: &FullViewingKey) -> Self {
|
||||
todo!()
|
||||
fn from(fvk: &FullViewingKey) -> Self {
|
||||
fvk.derive_dk_ovk().1
|
||||
}
|
||||
}
|
||||
|
||||
/// The diversified transmission key for a given payment address.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);
|
||||
|
||||
impl DiversifiedTransmissionKey {
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
|
||||
let g_d = diversify_hash(&d.0);
|
||||
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
//! # orchard
|
||||
//!
|
||||
//! ## Nomenclature
|
||||
//!
|
||||
//! All types in the `orchard` crate, unless otherwise specified, are Orchard-specific
|
||||
//! types. For example, [`Address`] is documented as being a shielded payment address; we
|
||||
//! implicitly mean it is an Orchard payment address (as opposed to e.g. a Sapling payment
|
||||
//! address, which is also shielded).
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
// Catch documentation errors caused by code changes.
|
||||
|
@ -10,9 +17,11 @@
|
|||
mod address;
|
||||
pub mod bundle;
|
||||
mod circuit;
|
||||
mod constants;
|
||||
pub mod keys;
|
||||
mod note;
|
||||
pub mod primitives;
|
||||
mod spec;
|
||||
mod tree;
|
||||
pub mod value;
|
||||
|
||||
|
|
|
@ -1,38 +1,75 @@
|
|||
//! TODO
|
||||
//! A minimal RedPallas implementation for use in Zcash.
|
||||
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
/// A RedPallas signature type.
|
||||
pub trait SigType: private::Sealed + fmt::Debug {}
|
||||
pub trait SigType: reddsa::SigType + private::Sealed {}
|
||||
|
||||
/// A type variable corresponding to an Orchard spend authorization signature.
|
||||
#[derive(Debug)]
|
||||
pub enum SpendAuth {}
|
||||
pub type SpendAuth = reddsa::orchard::SpendAuth;
|
||||
impl SigType for SpendAuth {}
|
||||
|
||||
/// A type variable corresponding to an Orchard binding signature.
|
||||
#[derive(Debug)]
|
||||
pub enum Binding {}
|
||||
pub type Binding = reddsa::orchard::Binding;
|
||||
impl SigType for Binding {}
|
||||
|
||||
/// A RedPallas signing key.
|
||||
#[derive(Debug)]
|
||||
pub struct SigningKey<T: SigType> {
|
||||
_t: PhantomData<T>,
|
||||
pub struct SigningKey<T: SigType>(reddsa::SigningKey<T>);
|
||||
|
||||
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
|
||||
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
||||
sk.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> From<&SigningKey<T>> for [u8; 32] {
|
||||
fn from(sk: &SigningKey<T>) -> [u8; 32] {
|
||||
sk.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
|
||||
type Error = reddsa::Error;
|
||||
|
||||
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
||||
bytes.try_into().map(SigningKey)
|
||||
}
|
||||
}
|
||||
|
||||
/// A RedPallas verification key.
|
||||
#[derive(Debug)]
|
||||
pub struct VerificationKey<T: SigType> {
|
||||
_t: PhantomData<T>,
|
||||
pub struct VerificationKey<T: SigType>(reddsa::VerificationKey<T>);
|
||||
|
||||
impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
|
||||
fn from(vk: VerificationKey<T>) -> [u8; 32] {
|
||||
vk.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> From<&VerificationKey<T>> for [u8; 32] {
|
||||
fn from(vk: &VerificationKey<T>) -> [u8; 32] {
|
||||
vk.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
|
||||
type Error = reddsa::Error;
|
||||
|
||||
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
||||
bytes.try_into().map(VerificationKey)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SigType> From<&'a SigningKey<T>> for VerificationKey<T> {
|
||||
fn from(sk: &'a SigningKey<T>) -> VerificationKey<T> {
|
||||
VerificationKey((&sk.0).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A RedPallas signature.
|
||||
#[derive(Debug)]
|
||||
pub struct Signature<T: SigType> {
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
pub struct Signature<T: SigType>(reddsa::Signature<T>);
|
||||
|
||||
pub(crate) mod private {
|
||||
use super::{Binding, SpendAuth};
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
//! The Sinsemilla hash function and commitment scheme.
|
||||
|
||||
use group::{Curve, Group};
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, CurveExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
use group::Group;
|
||||
use halo2::{arithmetic::CurveExt, pasta::pallas};
|
||||
|
||||
use crate::spec::extract_p;
|
||||
|
||||
const GROUP_HASH_Q: &str = "z.cash:SinsemillaQ";
|
||||
const GROUP_HASH_S: &str = "z.cash:SinsemillaS";
|
||||
|
@ -19,9 +18,16 @@ fn lebs2ip_k(bits: &[bool]) -> u32 {
|
|||
.fold(0u32, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
||||
}
|
||||
|
||||
/// Pads the given iterator (which MUST have length $\leq K * C$) with zero-bits to a
|
||||
/// multiple of $K$ bits.
|
||||
struct Pad<I: Iterator<Item = bool>> {
|
||||
/// The iterator we are padding.
|
||||
inner: I,
|
||||
/// The measured length of the inner iterator.
|
||||
///
|
||||
/// This starts as a lower bound, and will be accurate once `padding_left.is_some()`.
|
||||
len: usize,
|
||||
/// The amount of padding that remains to be emitted.
|
||||
padding_left: Option<usize>,
|
||||
}
|
||||
|
||||
|
@ -40,21 +46,28 @@ impl<I: Iterator<Item = bool>> Iterator for Pad<I> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
// If we have identified the required padding, the inner iterator has ended,
|
||||
// and we will never poll it again.
|
||||
if let Some(n) = self.padding_left.as_mut() {
|
||||
if *n == 0 {
|
||||
// Either we already emitted all necessary padding, or there was no
|
||||
// padding required.
|
||||
break None;
|
||||
} else {
|
||||
// Emit the next padding bit.
|
||||
*n -= 1;
|
||||
break Some(false);
|
||||
}
|
||||
} else if let Some(ret) = self.inner.next() {
|
||||
// We haven't reached the end of the inner iterator yet.
|
||||
self.len += 1;
|
||||
assert!(self.len <= K * C);
|
||||
break Some(ret);
|
||||
} else {
|
||||
// Inner iterator just ended.
|
||||
// Inner iterator just ended, so we now know its length.
|
||||
let rem = self.len % K;
|
||||
if rem > 0 {
|
||||
// The inner iterator requires padding in the range [1,K).
|
||||
self.padding_left = Some(K - rem);
|
||||
} else {
|
||||
// No padding required.
|
||||
|
@ -65,27 +78,14 @@ impl<I: Iterator<Item = bool>> Iterator for Pad<I> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Hash extractor for Pallas, from [§ 5.4.8.7].
|
||||
///
|
||||
/// [§ 5.4.8.7]: https://zips.z.cash/protocol/nu5.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].
|
||||
/// `SinsemillaHashToPoint` from [§ 5.4.1.9][concretesinsemillahash].
|
||||
///
|
||||
/// [§ 5.4.1.9]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillahash
|
||||
/// [concretesinsemillahash]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillahash
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn hash_to_point(domain_prefix: &str, msg: impl Iterator<Item = bool>) -> pallas::Point {
|
||||
let padded: Vec<_> = Pad::new(msg).collect();
|
||||
|
@ -98,16 +98,16 @@ pub(crate) fn hash_to_point(domain_prefix: &str, msg: impl Iterator<Item = bool>
|
|||
.fold(Q(domain_prefix), |acc, chunk| acc.double() + S(chunk))
|
||||
}
|
||||
|
||||
/// `SinsemillaHash` from [§ 5.4.1.9].
|
||||
/// `SinsemillaHash` from [§ 5.4.1.9][concretesinsemillahash].
|
||||
///
|
||||
/// [§ 5.4.1.9]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillahash
|
||||
/// [concretesinsemillahash]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillahash
|
||||
pub(crate) fn hash(domain_prefix: &str, msg: impl Iterator<Item = bool>) -> pallas::Base {
|
||||
extract(&hash_to_point(domain_prefix, msg))
|
||||
extract_p(&hash_to_point(domain_prefix, msg))
|
||||
}
|
||||
|
||||
/// `SinsemillaCommit` from [§ 5.4.7.4].
|
||||
/// `SinsemillaCommit` from [§ 5.4.8.4][concretesinsemillacommit].
|
||||
///
|
||||
/// [§ 5.4.7.4]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
|
||||
/// [concretesinsemillacommit]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn commit(
|
||||
domain_prefix: &str,
|
||||
|
@ -122,15 +122,15 @@ pub(crate) fn commit(
|
|||
hash_to_point(&m_prefix, msg) + hasher_r(&[]) * r
|
||||
}
|
||||
|
||||
/// `SinsemillaShortCommit` from [§ 5.4.7.4].
|
||||
/// `SinsemillaShortCommit` from [§ 5.4.8.4][concretesinsemillacommit].
|
||||
///
|
||||
/// [§ 5.4.7.4]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
|
||||
/// [concretesinsemillacommit]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
|
||||
pub(crate) fn short_commit(
|
||||
domain_prefix: &str,
|
||||
msg: impl Iterator<Item = bool>,
|
||||
r: &pallas::Scalar,
|
||||
) -> pallas::Base {
|
||||
extract(&commit(domain_prefix, msg, r))
|
||||
extract_p(&commit(domain_prefix, msg, r))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
//! Helper functions defined in the Zcash Protocol Specification.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use blake2b_simd::Params;
|
||||
use ff::PrimeField;
|
||||
use group::{Curve, Group};
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, CurveExt, FieldExt},
|
||||
pasta::pallas,
|
||||
};
|
||||
|
||||
use crate::{constants::L_ORCHARD_BASE, primitives::sinsemilla};
|
||||
|
||||
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
|
||||
|
||||
/// $\mathsf{ToBase}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod q_P)$
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
pub(crate) fn to_base(x: [u8; 64]) -> pallas::Base {
|
||||
pallas::Base::from_bytes_wide(&x)
|
||||
}
|
||||
|
||||
/// $\mathsf{ToScalar}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod r_P)$
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar {
|
||||
pallas::Scalar::from_bytes_wide(&x)
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
pub(crate) fn commit_ivk(
|
||||
ak: &pallas::Base,
|
||||
nk: &pallas::Base,
|
||||
rivk: &pallas::Scalar,
|
||||
) -> pallas::Scalar {
|
||||
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
||||
// bits, which is equal to L_ORCHARD_BASE.
|
||||
let ivk = sinsemilla::short_commit(
|
||||
"z.cash:Orchard-CommitIvk",
|
||||
iter::empty()
|
||||
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
||||
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
||||
rivk,
|
||||
);
|
||||
|
||||
// Convert from pallas::Base to pallas::Scalar. This requires no modular reduction
|
||||
// because Pallas' base field is smaller than its scalar field.
|
||||
pallas::Scalar::from_repr(ivk.to_repr()).unwrap()
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
||||
///
|
||||
/// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash
|
||||
pub(crate) fn diversify_hash(d: &[u8; 11]) -> pallas::Point {
|
||||
let hasher = pallas::Point::hash_to_curve("z.cash:Orchard-gd");
|
||||
let pk_d = hasher(d);
|
||||
if pk_d.is_identity().into() {
|
||||
// If the identity occurs, we replace it with a different fixed point.
|
||||
hasher(&[])
|
||||
} else {
|
||||
pk_d
|
||||
}
|
||||
}
|
||||
|
||||
/// $PRF^\mathsf{expand}(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)$
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
|
||||
///
|
||||
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
|
||||
pub(crate) fn prf_expand(sk: &[u8], t: &[u8]) -> [u8; 64] {
|
||||
prf_expand_vec(sk, &[t])
|
||||
}
|
||||
|
||||
pub(crate) fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> [u8; 64] {
|
||||
let mut h = Params::new()
|
||||
.hash_length(64)
|
||||
.personal(PRF_EXPAND_PERSONALIZATION)
|
||||
.to_state();
|
||||
h.update(sk);
|
||||
for t in ts {
|
||||
h.update(t);
|
||||
}
|
||||
*h.finalize().as_array()
|
||||
}
|
||||
|
||||
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
||||
///
|
||||
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
||||
pub(crate) fn ka_orchard(sk: &pallas::Scalar, b: &pallas::Point) -> pallas::Point {
|
||||
b * sk
|
||||
}
|
||||
|
||||
/// Coordinate extractor for Pallas.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
|
||||
///
|
||||
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
|
||||
pub(crate) fn extract_p(point: &pallas::Point) -> pallas::Base {
|
||||
if let Some((x, _)) = point.to_affine().get_xy().into() {
|
||||
x
|
||||
} else {
|
||||
pallas::Base::zero()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::Group;
|
||||
use halo2::{arithmetic::CurveExt, pasta::pallas};
|
||||
|
||||
#[test]
|
||||
fn diversify_hash_substitution() {
|
||||
assert!(!bool::from(
|
||||
pallas::Point::hash_to_curve("z.cash:Orchard-gd")(&[]).is_identity()
|
||||
));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue