2021-01-20 10:54:00 -08:00
|
|
|
//! Key structures for Orchard.
|
|
|
|
|
2021-03-05 15:25:45 -08:00
|
|
|
use std::convert::TryInto;
|
|
|
|
use std::mem;
|
|
|
|
|
2021-03-05 15:36:38 -08:00
|
|
|
use aes::Aes256;
|
|
|
|
use fpe::ff1::{BinaryNumeralString, FF1};
|
2021-03-05 15:25:45 -08:00
|
|
|
use group::GroupEncoding;
|
|
|
|
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
|
|
|
use subtle::CtOption;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
address::Address,
|
2021-03-05 16:03:26 -08:00
|
|
|
primitives::redpallas::{self, SpendAuth},
|
2021-03-05 15:25:45 -08:00
|
|
|
spec::{
|
2021-03-08 11:28:28 -08:00
|
|
|
commit_ivk, diversify_hash, ka_orchard, prf_expand, prf_expand_vec, to_base, to_scalar,
|
2021-03-05 15:25:45 -08:00
|
|
|
},
|
|
|
|
};
|
2021-01-20 10:54:00 -08:00
|
|
|
|
|
|
|
/// A spending key, from which all key material is derived.
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-03-05 15:25:45 -08:00
|
|
|
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())
|
|
|
|
}
|
|
|
|
}
|
2021-01-20 10:54:00 -08:00
|
|
|
|
2021-03-05 16:58:48 -08:00
|
|
|
/// A spend authorizing key, used to create spend authorization signatures.
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-03-05 16:03:26 -08:00
|
|
|
pub(crate) struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
|
2021-03-05 15:25:45 -08:00
|
|
|
|
|
|
|
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]))
|
|
|
|
}
|
|
|
|
}
|
2021-01-20 10:54:00 -08:00
|
|
|
|
2021-01-21 04:16:50 -08:00
|
|
|
impl From<&SpendingKey> for SpendAuthorizingKey {
|
2021-03-05 15:25:45 -08:00
|
|
|
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.
|
2021-03-08 12:20:09 -08:00
|
|
|
if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 {
|
2021-03-05 15:25:45 -08:00
|
|
|
SpendAuthorizingKey((-ask).to_bytes().try_into().unwrap())
|
|
|
|
} else {
|
|
|
|
ret
|
|
|
|
}
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 12:20:09 -08:00
|
|
|
/// A key used to validate spend authorization signatures.
|
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-03-08 12:20:09 -08:00
|
|
|
pub(crate) struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
2021-01-20 10:54:00 -08:00
|
|
|
|
2021-03-08 12:20:09 -08:00
|
|
|
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
2021-03-05 15:25:45 -08:00
|
|
|
fn from(ask: &SpendAuthorizingKey) -> Self {
|
2021-03-08 12:20:09 -08:00
|
|
|
SpendValidatingKey((&ask.0).into())
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 15:25:45 -08:00
|
|
|
/// A key used to derive [`Nullifier`]s from [`Note`]s.
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-08 12:27:34 -08:00
|
|
|
/// [`Nullifier`]: crate::note::Nullifier
|
|
|
|
/// [`Note`]: crate::note::Note
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-03-05 15:25:45 -08:00
|
|
|
pub(crate) struct NullifierDerivingKey(pallas::Base);
|
2021-01-20 10:54:00 -08:00
|
|
|
|
2021-01-21 04:16:50 -08:00
|
|
|
impl From<&SpendingKey> for NullifierDerivingKey {
|
2021-03-05 15:25:45 -08:00
|
|
|
fn from(sk: &SpendingKey) -> Self {
|
|
|
|
NullifierDerivingKey(to_base(prf_expand(&sk.0, &[0x07])))
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 17:24:45 -08:00
|
|
|
/// 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])))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-20 10:54:00 -08:00
|
|
|
/// A key that provides the capability to view incoming and outgoing transactions.
|
|
|
|
///
|
2021-02-03 06:19:29 -08:00
|
|
|
/// 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).
|
2021-03-08 12:22:38 -08:00
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-01-21 04:16:50 -08:00
|
|
|
pub struct FullViewingKey {
|
2021-03-08 12:20:09 -08:00
|
|
|
ak: SpendValidatingKey,
|
2021-01-21 04:16:50 -08:00
|
|
|
nk: NullifierDerivingKey,
|
2021-03-05 17:24:45 -08:00
|
|
|
rivk: CommitIvkRandomness,
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
|
2021-01-21 04:16:50 -08:00
|
|
|
impl From<&SpendingKey> for FullViewingKey {
|
2021-03-05 15:25:45 -08:00
|
|
|
fn from(sk: &SpendingKey) -> Self {
|
|
|
|
FullViewingKey {
|
|
|
|
ak: (&SpendAuthorizingKey::from(sk)).into(),
|
|
|
|
nk: sk.into(),
|
2021-03-05 17:24:45 -08:00
|
|
|
rivk: sk.into(),
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 04:16:50 -08:00
|
|
|
impl FullViewingKey {
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-05 15:25:45 -08:00
|
|
|
fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
|
2021-03-05 17:24:45 -08:00
|
|
|
let k = self.rivk.0.to_bytes();
|
2021-03-05 16:03:26 -08:00
|
|
|
let b = [(&self.ak.0).into(), self.nk.0.to_bytes()];
|
2021-03-05 15:25:45 -08:00
|
|
|
let r = prf_expand_vec(&k, &[&[0x82], &b[0][..], &b[1][..]]);
|
|
|
|
(
|
|
|
|
DiversifierKey(r.as_bytes()[..32].try_into().unwrap()),
|
|
|
|
OutgoingViewingKey(r.as_bytes()[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())
|
|
|
|
}
|
|
|
|
|
2021-01-20 10:54:00 -08:00
|
|
|
/// Returns the payment address for this key corresponding to the given diversifier.
|
2021-01-21 04:16:50 -08:00
|
|
|
pub fn address(&self, d: Diversifier) -> Address {
|
2021-01-20 10:54:00 -08:00
|
|
|
IncomingViewingKey::from(self).address(d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 15:25:45 -08:00
|
|
|
/// A key that provides the capability to derive a sequence of diversifiers.
|
2021-03-08 12:22:38 -08:00
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-05 15:25:45 -08:00
|
|
|
#[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 {
|
2021-03-08 10:39:25 -08:00
|
|
|
self.get(0u32)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the diversifier at the given index.
|
2021-03-05 15:36:38 -08:00
|
|
|
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())
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-20 10:54:00 -08:00
|
|
|
/// A diversifier that can be used to derive a specific [`Address`] from a
|
|
|
|
/// [`FullViewingKey`] or [`IncomingViewingKey`].
|
2021-03-08 12:22:38 -08:00
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-02-03 06:16:58 -08:00
|
|
|
pub struct Diversifier([u8; 11]);
|
2021-01-20 10:54:00 -08:00
|
|
|
|
|
|
|
/// A key that provides the capability to detect and decrypt incoming notes from the block
|
|
|
|
/// chain, without being able to spend the notes or detect when they are spent.
|
|
|
|
///
|
|
|
|
/// This key is useful in situations where you only need the capability to detect inbound
|
|
|
|
/// payments, such as merchant terminals.
|
|
|
|
///
|
2021-02-08 07:01:34 -08:00
|
|
|
/// 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.
|
2021-03-08 12:22:38 -08:00
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-01-20 10:54:00 -08:00
|
|
|
#[derive(Debug)]
|
2021-03-05 15:25:45 -08:00
|
|
|
pub struct IncomingViewingKey(pallas::Scalar);
|
2021-01-20 10:54:00 -08:00
|
|
|
|
2021-01-21 04:16:50 -08:00
|
|
|
impl From<&FullViewingKey> for IncomingViewingKey {
|
2021-03-05 15:25:45 -08:00
|
|
|
fn from(fvk: &FullViewingKey) -> Self {
|
2021-03-05 16:03:26 -08:00
|
|
|
let ak = pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap();
|
2021-03-08 11:28:28 -08:00
|
|
|
IncomingViewingKey(commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0))
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 04:16:50 -08:00
|
|
|
impl IncomingViewingKey {
|
2021-01-20 10:54:00 -08:00
|
|
|
/// Returns the payment address for this key corresponding to the given diversifier.
|
2021-03-05 15:25:45 -08:00
|
|
|
pub fn address(&self, d: Diversifier) -> Address {
|
2021-03-05 17:03:53 -08:00
|
|
|
let pk_d = DiversifiedTransmissionKey::derive(self, &d);
|
|
|
|
Address::from_parts(d, pk_d)
|
2021-01-20 10:54:00 -08:00
|
|
|
}
|
|
|
|
}
|
2021-02-08 07:01:34 -08:00
|
|
|
|
|
|
|
/// A key that provides the capability to recover outgoing transaction information from
|
|
|
|
/// the block chain.
|
|
|
|
///
|
|
|
|
/// 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.
|
2021-03-08 12:22:38 -08:00
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-02-08 07:01:34 -08:00
|
|
|
#[derive(Debug)]
|
2021-03-05 15:25:45 -08:00
|
|
|
pub struct OutgoingViewingKey([u8; 32]);
|
2021-02-08 07:01:34 -08:00
|
|
|
|
|
|
|
impl From<&FullViewingKey> for OutgoingViewingKey {
|
2021-03-05 15:25:45 -08:00
|
|
|
fn from(fvk: &FullViewingKey) -> Self {
|
|
|
|
fvk.derive_dk_ovk().1
|
2021-02-08 07:01:34 -08:00
|
|
|
}
|
|
|
|
}
|
2021-03-05 17:03:53 -08:00
|
|
|
|
|
|
|
/// The diversified transmission key for a given payment address.
|
2021-03-08 12:22:38 -08:00
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
|
|
|
///
|
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-05 17:03:53 -08:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);
|
|
|
|
|
|
|
|
impl DiversifiedTransmissionKey {
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 17:03:53 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-05 17:03:53 -08:00
|
|
|
fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
|
|
|
|
let g_d = diversify_hash(&d.0);
|
|
|
|
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
|
|
|
|
}
|
|
|
|
}
|