zebra_chain/orchard/
keys.rs

1//! Orchard key types.
2//!
3//! Unused key types are not implemented, see PR #5476.
4//!
5//! <https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents>
6
7use std::{fmt, io};
8
9use group::{ff::PrimeField, prime::PrimeCurveAffine, Group, GroupEncoding};
10use halo2::{
11    arithmetic::{Coordinates, CurveAffine},
12    pasta::pallas,
13};
14use rand_core::{CryptoRng, RngCore};
15
16use crate::{
17    error::RandError,
18    serialization::{
19        serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
20    },
21};
22
23use super::sinsemilla::*;
24
25/// Used to derive a diversified base point from a diversifier value.
26///
27/// DiversifyHash^Orchard(d) := {︃ GroupHash^P("z.cash:Orchard-gd",""), if P = 0_P
28///                               P,                                   otherwise
29///
30/// where P = GroupHash^P(("z.cash:Orchard-gd", LEBS2OSP_l_d(d)))
31///
32/// <https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash>
33fn diversify_hash(d: &[u8]) -> pallas::Point {
34    let p = pallas_group_hash(b"z.cash:Orchard-gd", d);
35
36    if <bool>::from(p.is_identity()) {
37        pallas_group_hash(b"z.cash:Orchard-gd", b"")
38    } else {
39        p
40    }
41}
42
43/// A _diversifier_, as described in [protocol specification §4.2.3][ps].
44///
45/// [ps]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
46#[derive(Copy, Clone, Eq, PartialEq)]
47#[cfg_attr(
48    any(test, feature = "proptest-impl"),
49    derive(proptest_derive::Arbitrary)
50)]
51pub struct Diversifier(pub(crate) [u8; 11]);
52
53impl fmt::Debug for Diversifier {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        f.debug_tuple("Diversifier")
56            .field(&hex::encode(self.0))
57            .finish()
58    }
59}
60
61impl From<[u8; 11]> for Diversifier {
62    fn from(bytes: [u8; 11]) -> Self {
63        Self(bytes)
64    }
65}
66
67impl From<Diversifier> for [u8; 11] {
68    fn from(d: Diversifier) -> [u8; 11] {
69        d.0
70    }
71}
72
73impl From<Diversifier> for pallas::Point {
74    /// Derive a _diversified base_ point.
75    ///
76    /// g_d := DiversifyHash^Orchard(d)
77    ///
78    /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
79    fn from(d: Diversifier) -> Self {
80        diversify_hash(&d.0)
81    }
82}
83
84impl PartialEq<[u8; 11]> for Diversifier {
85    fn eq(&self, other: &[u8; 11]) -> bool {
86        self.0 == *other
87    }
88}
89
90impl From<Diversifier> for pallas::Affine {
91    /// Get a diversified base point from a diversifier value in affine
92    /// representation.
93    fn from(d: Diversifier) -> Self {
94        let projective_point = pallas::Point::from(d);
95        projective_point.into()
96    }
97}
98
99impl Diversifier {
100    /// Generate a new `Diversifier`.
101    ///
102    /// <https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents>
103    pub fn new<T>(csprng: &mut T) -> Result<Self, RandError>
104    where
105        T: RngCore + CryptoRng,
106    {
107        let mut bytes = [0u8; 11];
108        csprng
109            .try_fill_bytes(&mut bytes)
110            .map_err(|_| RandError::FillBytes)?;
111
112        Ok(Self::from(bytes))
113    }
114}
115
116/// A (diversified) transmission Key
117///
118/// In Orchard, secrets need to be transmitted to a recipient of funds in order
119/// for them to be later spent. To transmit these secrets securely to a
120/// recipient without requiring an out-of-band communication channel, the
121/// transmission key is used to encrypt them.
122///
123/// Derived by multiplying a Pallas point [derived][concretediversifyhash] from
124/// a `Diversifier` by the `IncomingViewingKey` scalar.
125///
126/// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash
127/// <https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents>
128#[derive(Copy, Clone, PartialEq)]
129pub struct TransmissionKey(pub(crate) pallas::Affine);
130
131impl fmt::Debug for TransmissionKey {
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        let mut d = f.debug_struct("TransmissionKey");
134
135        let option: Option<Coordinates<pallas::Affine>> = self.0.coordinates().into();
136
137        match option {
138            Some(coordinates) => d
139                .field("x", &hex::encode(coordinates.x().to_repr()))
140                .field("y", &hex::encode(coordinates.y().to_repr()))
141                .finish(),
142            None => d
143                .field("x", &hex::encode(pallas::Base::zero().to_repr()))
144                .field("y", &hex::encode(pallas::Base::zero().to_repr()))
145                .finish(),
146        }
147    }
148}
149
150impl Eq for TransmissionKey {}
151
152impl From<TransmissionKey> for [u8; 32] {
153    fn from(pk_d: TransmissionKey) -> [u8; 32] {
154        pk_d.0.to_bytes()
155    }
156}
157
158impl PartialEq<[u8; 32]> for TransmissionKey {
159    fn eq(&self, other: &[u8; 32]) -> bool {
160        &self.0.to_bytes() == other
161    }
162}
163
164/// An ephemeral public key for Orchard key agreement.
165///
166/// <https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement>
167/// <https://zips.z.cash/protocol/nu5.pdf#saplingandorchardencrypt>
168#[derive(Copy, Clone, Deserialize, PartialEq, Eq, Serialize)]
169pub struct EphemeralPublicKey(#[serde(with = "serde_helpers::Affine")] pub(crate) pallas::Affine);
170
171impl fmt::Debug for EphemeralPublicKey {
172    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173        let mut d = f.debug_struct("EphemeralPublicKey");
174
175        let option: Option<Coordinates<pallas::Affine>> = self.0.coordinates().into();
176
177        match option {
178            Some(coordinates) => d
179                .field("x", &hex::encode(coordinates.x().to_repr()))
180                .field("y", &hex::encode(coordinates.y().to_repr()))
181                .finish(),
182            None => d
183                .field("x", &hex::encode(pallas::Base::zero().to_repr()))
184                .field("y", &hex::encode(pallas::Base::zero().to_repr()))
185                .finish(),
186        }
187    }
188}
189
190impl From<EphemeralPublicKey> for [u8; 32] {
191    fn from(epk: EphemeralPublicKey) -> [u8; 32] {
192        epk.0.to_bytes()
193    }
194}
195
196impl From<&EphemeralPublicKey> for [u8; 32] {
197    fn from(epk: &EphemeralPublicKey) -> [u8; 32] {
198        epk.0.to_bytes()
199    }
200}
201
202impl PartialEq<[u8; 32]> for EphemeralPublicKey {
203    fn eq(&self, other: &[u8; 32]) -> bool {
204        &self.0.to_bytes() == other
205    }
206}
207
208impl TryFrom<[u8; 32]> for EphemeralPublicKey {
209    type Error = &'static str;
210
211    /// Convert an array into a [`EphemeralPublicKey`].
212    ///
213    /// Returns an error if the encoding is malformed or if [it encodes the
214    /// identity point][1].
215    ///
216    /// > epk cannot be 𝒪_P
217    ///
218    /// Note that this is [intrinsic to the EphemeralPublicKey][2] type and it is not
219    /// a separate consensus rule:
220    ///
221    /// > Define KA^{Orchard}.Public := P^*.
222    ///
223    /// [1]: https://zips.z.cash/protocol/protocol.pdf#actiondesc
224    /// [2]: https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement
225    fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
226        let possible_point = pallas::Affine::from_bytes(&bytes);
227
228        if possible_point.is_some().into() {
229            let point = possible_point.unwrap();
230            if point.to_curve().is_identity().into() {
231                Err("pallas::Affine value for Orchard EphemeralPublicKey is the identity")
232            } else {
233                Ok(Self(possible_point.unwrap()))
234            }
235        } else {
236            Err("Invalid pallas::Affine value for Orchard EphemeralPublicKey")
237        }
238    }
239}
240
241impl ZcashSerialize for EphemeralPublicKey {
242    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
243        writer.write_all(&<[u8; 32]>::from(self)[..])?;
244        Ok(())
245    }
246}
247
248impl ZcashDeserialize for EphemeralPublicKey {
249    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
250        Self::try_from(reader.read_32_bytes()?).map_err(SerializationError::Parse)
251    }
252}