Add Sapling EphemeralPublicKey type that wraps jubjub::AffinePoint

This commit is contained in:
Deirdre Connolly 2020-08-12 01:43:57 -04:00 committed by Deirdre Connolly
parent 52a10d2837
commit 6b13ce3e83
5 changed files with 86 additions and 13 deletions

View File

@ -9,6 +9,8 @@
//! [3.1]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys //! [3.1]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
#[cfg(test)]
mod arbitrary;
#[cfg(test)] #[cfg(test)]
mod test_vectors; mod test_vectors;
#[cfg(test)] #[cfg(test)]
@ -29,7 +31,8 @@ use proptest_derive::Arbitrary;
use crate::{ use crate::{
redjubjub::{self, SpendAuth}, redjubjub::{self, SpendAuth},
serialization::{ReadZcashExt, SerializationError}, serde_helpers,
serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize},
Network, Network,
}; };
@ -860,3 +863,59 @@ impl FromStr for FullViewingKey {
} }
} }
} }
/// An ephemeral public key for Sapling key agreement.
///
/// https://zips.z.cash/protocol/canopy.pdf#concretesaplingkeyagreement
#[derive(Copy, Clone, Deserialize, PartialEq, Serialize)]
pub struct EphemeralPublicKey(#[serde(with = "serde_helpers::AffinePoint")] jubjub::AffinePoint);
impl fmt::Debug for EphemeralPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EphemeralPublicKey")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.finish()
}
}
impl Eq for EphemeralPublicKey {}
impl From<&EphemeralPublicKey> for [u8; 32] {
fn from(nk: &EphemeralPublicKey) -> [u8; 32] {
nk.0.to_bytes()
}
}
impl PartialEq<[u8; 32]> for EphemeralPublicKey {
fn eq(&self, other: &[u8; 32]) -> bool {
<[u8; 32]>::from(self) == *other
}
}
impl TryFrom<[u8; 32]> for EphemeralPublicKey {
type Error = &'static str;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let possible_point = jubjub::AffinePoint::from_bytes(bytes);
if possible_point.is_some().into() {
Ok(Self(possible_point.unwrap()))
} else {
Err("Invalid jubjub::AffinePoint value")
}
}
}
impl ZcashSerialize for EphemeralPublicKey {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&<[u8; 32]>::from(self)[..])?;
Ok(())
}
}
impl ZcashDeserialize for EphemeralPublicKey {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Self::try_from(reader.read_32_bytes()?).map_err(|e| SerializationError::Parse(e))
}
}

View File

@ -0,0 +1,17 @@
use std::convert::TryFrom;
use proptest::{arbitrary::any, array, prelude::*};
use crate::keys::sapling;
impl Arbitrary for sapling::EphemeralPublicKey {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
array::uniform32(any::<u8>())
.prop_filter_map("Valid jubjub::AffinePoint", |b| Self::try_from(b).ok())
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}

View File

@ -9,7 +9,7 @@ use std::{
}; };
use crate::{ use crate::{
commitments, notes, commitments, keys, notes,
proofs::ZkSnarkProof, proofs::ZkSnarkProof,
serialization::{ serialization::{
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize, ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
@ -352,7 +352,7 @@ impl ZcashSerialize for Output {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.cv.zcash_serialize(&mut writer)?; self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.cm_u.to_bytes())?; writer.write_all(&self.cm_u.to_bytes())?;
writer.write_all(&self.ephemeral_key.to_bytes())?; self.ephemeral_key.zcash_serialize(&mut writer)?;
self.enc_ciphertext.zcash_serialize(&mut writer)?; self.enc_ciphertext.zcash_serialize(&mut writer)?;
self.out_ciphertext.zcash_serialize(&mut writer)?; self.out_ciphertext.zcash_serialize(&mut writer)?;
self.zkproof.zcash_serialize(&mut writer)?; self.zkproof.zcash_serialize(&mut writer)?;
@ -365,7 +365,7 @@ impl ZcashDeserialize for Output {
Ok(Output { Ok(Output {
cv: commitments::sapling::ValueCommitment::zcash_deserialize(&mut reader)?, cv: commitments::sapling::ValueCommitment::zcash_deserialize(&mut reader)?,
cm_u: jubjub::Fq::from_bytes(&reader.read_32_bytes()?).unwrap(), cm_u: jubjub::Fq::from_bytes(&reader.read_32_bytes()?).unwrap(),
ephemeral_key: jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(), ephemeral_key: keys::sapling::EphemeralPublicKey::zcash_deserialize(&mut reader)?,
enc_ciphertext: notes::sapling::EncryptedCiphertext::zcash_deserialize(&mut reader)?, enc_ciphertext: notes::sapling::EncryptedCiphertext::zcash_deserialize(&mut reader)?,
out_ciphertext: notes::sapling::OutCiphertext::zcash_deserialize(&mut reader)?, out_ciphertext: notes::sapling::OutCiphertext::zcash_deserialize(&mut reader)?,
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?, zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
commitments, notes, commitments, keys, notes,
proofs::Groth16Proof, proofs::Groth16Proof,
redjubjub::{self, Binding, SpendAuth}, redjubjub::{self, Binding, SpendAuth},
serde_helpers, serde_helpers,
@ -37,8 +37,7 @@ pub struct Output {
#[serde(with = "serde_helpers::Fq")] #[serde(with = "serde_helpers::Fq")]
pub cm_u: jubjub::Fq, pub cm_u: jubjub::Fq,
/// An encoding of an ephemeral Jubjub public key. /// An encoding of an ephemeral Jubjub public key.
#[serde(with = "serde_helpers::AffinePoint")] pub ephemeral_key: keys::sapling::EphemeralPublicKey,
pub ephemeral_key: jubjub::AffinePoint,
/// A ciphertext component for the encrypted output note. /// A ciphertext component for the encrypted output note.
pub enc_ciphertext: notes::sapling::EncryptedCiphertext, pub enc_ciphertext: notes::sapling::EncryptedCiphertext,
/// A ciphertext component for the encrypted output note. /// A ciphertext component for the encrypted output note.

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
commitments, commitments, keys,
notes::{sapling, sprout}, notes::{sapling, sprout},
proofs::{Groth16Proof, ZkSnarkProof}, proofs::{Groth16Proof, ZkSnarkProof},
transaction::{ transaction::{
@ -97,18 +97,16 @@ impl Arbitrary for Output {
( (
any::<commitments::sapling::ValueCommitment>(), any::<commitments::sapling::ValueCommitment>(),
any::<commitments::sapling::NoteCommitment>(), any::<commitments::sapling::NoteCommitment>(),
array::uniform32(any::<u8>()).prop_filter("Valid jubjub::AffinePoint", |b| { any::<keys::sapling::EphemeralPublicKey>(),
jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1
}),
any::<sapling::EncryptedCiphertext>(), any::<sapling::EncryptedCiphertext>(),
any::<sapling::OutCiphertext>(), any::<sapling::OutCiphertext>(),
any::<Groth16Proof>(), any::<Groth16Proof>(),
) )
.prop_map( .prop_map(
|(cv, cm, ephemeral_key_bytes, enc_ciphertext, out_ciphertext, zkproof)| Self { |(cv, cm, ephemeral_key, enc_ciphertext, out_ciphertext, zkproof)| Self {
cv, cv,
cm_u: cm.extract_u(), cm_u: cm.extract_u(),
ephemeral_key: jubjub::AffinePoint::from_bytes(ephemeral_key_bytes).unwrap(), ephemeral_key,
enc_ciphertext, enc_ciphertext,
out_ciphertext, out_ciphertext,
zkproof, zkproof,