use std::{fmt, io};
use rand_core::{CryptoRng, RngCore};
use crate::{
error::{AddressError, RandError},
primitives::redjubjub::SpendAuth,
serialization::{
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
},
};
#[cfg(test)]
mod test_vectors;
pub(super) const RANDOMNESS_BEACON_URS: &[u8; 64] =
b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
fn jubjub_group_hash(d: [u8; 8], m: &[u8]) -> Option<jubjub::ExtendedPoint> {
let hash = blake2s_simd::Params::new()
.hash_length(32)
.personal(&d)
.to_state()
.update(RANDOMNESS_BEACON_URS)
.update(m)
.finalize();
let ct_option = jubjub::AffinePoint::from_bytes(*hash.as_array());
if ct_option.is_some().unwrap_u8() == 1 {
let extended_point = ct_option.unwrap().mul_by_cofactor();
if extended_point != jubjub::ExtendedPoint::identity() {
Some(extended_point)
} else {
None
}
} else {
None
}
}
fn diversify_hash(d: [u8; 11]) -> Option<jubjub::ExtendedPoint> {
jubjub_group_hash(*b"Zcash_gd", &d)
}
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub struct Diversifier(pub(crate) [u8; 11]);
impl fmt::Debug for Diversifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Diversifier")
.field(&hex::encode(self.0))
.finish()
}
}
impl From<[u8; 11]> for Diversifier {
fn from(bytes: [u8; 11]) -> Self {
Self(bytes)
}
}
impl From<Diversifier> for [u8; 11] {
fn from(d: Diversifier) -> [u8; 11] {
d.0
}
}
impl TryFrom<Diversifier> for jubjub::AffinePoint {
type Error = &'static str;
fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
if let Ok(extended_point) = jubjub::ExtendedPoint::try_from(d) {
Ok(extended_point.into())
} else {
Err("Invalid Diversifier -> jubjub::AffinePoint")
}
}
}
impl TryFrom<Diversifier> for jubjub::ExtendedPoint {
type Error = &'static str;
fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
let possible_point = diversify_hash(d.0);
if let Some(point) = possible_point {
Ok(point)
} else {
Err("Invalid Diversifier -> jubjub::ExtendedPoint")
}
}
}
impl PartialEq<[u8; 11]> for Diversifier {
fn eq(&self, other: &[u8; 11]) -> bool {
self.0 == *other
}
}
impl Diversifier {
pub fn new<T>(csprng: &mut T) -> Result<Self, AddressError>
where
T: RngCore + CryptoRng,
{
const DIVERSIFY_HASH_TRIES: u8 = 2;
for _ in 0..DIVERSIFY_HASH_TRIES {
let mut bytes = [0u8; 11];
csprng
.try_fill_bytes(&mut bytes)
.map_err(|_| AddressError::from(RandError::FillBytes))?;
if diversify_hash(bytes).is_some() {
return Ok(Self(bytes));
}
}
Err(AddressError::DiversifierGenerationFailure)
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct TransmissionKey(pub(crate) jubjub::AffinePoint);
impl fmt::Debug for TransmissionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TransmissionKey")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.finish()
}
}
impl Eq for TransmissionKey {}
impl TryFrom<[u8; 32]> for TransmissionKey {
type Error = &'static str;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let affine_point = jubjub::AffinePoint::from_bytes(bytes).unwrap();
if affine_point.is_torsion_free().into() {
Ok(Self(affine_point))
} else {
Err("Invalid jubjub::AffinePoint value for Sapling TransmissionKey")
}
}
}
impl From<TransmissionKey> for [u8; 32] {
fn from(pk_d: TransmissionKey) -> [u8; 32] {
pk_d.0.to_bytes()
}
}
impl PartialEq<[u8; 32]> for TransmissionKey {
fn eq(&self, other: &[u8; 32]) -> bool {
&self.0.to_bytes() == other
}
}
#[derive(Copy, Clone, Deserialize, PartialEq, Serialize)]
pub struct EphemeralPublicKey(
#[serde(with = "serde_helpers::AffinePoint")] pub(crate) 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 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 {
&self.0.to_bytes() == 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_none().into() {
return Err("Invalid jubjub::AffinePoint value for Sapling EphemeralPublicKey");
}
if possible_point.unwrap().is_small_order().into() {
Err("jubjub::AffinePoint value for Sapling EphemeralPublicKey point is of small order")
} else {
Ok(Self(possible_point.unwrap()))
}
}
}
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(SerializationError::Parse)
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct ValidatingKey(redjubjub::VerificationKey<SpendAuth>);
impl From<ValidatingKey> for redjubjub::VerificationKey<SpendAuth> {
fn from(rk: ValidatingKey) -> Self {
rk.0
}
}
impl TryFrom<redjubjub::VerificationKey<SpendAuth>> for ValidatingKey {
type Error = &'static str;
fn try_from(key: redjubjub::VerificationKey<SpendAuth>) -> Result<Self, Self::Error> {
if bool::from(
jubjub::AffinePoint::from_bytes(key.into())
.unwrap()
.is_small_order(),
) {
Err("jubjub::AffinePoint value for Sapling ValidatingKey is of small order")
} else {
Ok(Self(key))
}
}
}
impl TryFrom<[u8; 32]> for ValidatingKey {
type Error = &'static str;
fn try_from(value: [u8; 32]) -> Result<Self, Self::Error> {
let vk = redjubjub::VerificationKey::<SpendAuth>::try_from(value)
.map_err(|_| "Invalid redjubjub::ValidatingKey for Sapling ValidatingKey")?;
vk.try_into()
}
}
impl From<ValidatingKey> for [u8; 32] {
fn from(key: ValidatingKey) -> Self {
key.0.into()
}
}
impl From<ValidatingKey> for redjubjub::VerificationKeyBytes<SpendAuth> {
fn from(key: ValidatingKey) -> Self {
key.0.into()
}
}