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
#![allow(clippy::unit_arg)]
#[cfg(test)]
mod arbitrary;
#[cfg(test)]
mod test_vectors;
#[cfg(test)]
@ -29,7 +31,8 @@ use proptest_derive::Arbitrary;
use crate::{
redjubjub::{self, SpendAuth},
serialization::{ReadZcashExt, SerializationError},
serde_helpers,
serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize},
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::{
commitments, notes,
commitments, keys, notes,
proofs::ZkSnarkProof,
serialization::{
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> {
self.cv.zcash_serialize(&mut writer)?;
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.out_ciphertext.zcash_serialize(&mut writer)?;
self.zkproof.zcash_serialize(&mut writer)?;
@ -365,7 +365,7 @@ impl ZcashDeserialize for Output {
Ok(Output {
cv: commitments::sapling::ValueCommitment::zcash_deserialize(&mut reader)?,
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)?,
out_ciphertext: notes::sapling::OutCiphertext::zcash_deserialize(&mut reader)?,
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,

View File

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

View File

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