Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Orchard key components #40

Merged
merged 26 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f077979
Orchard key components
str4d Mar 5, 2021
ceac39d
Implement ZIP 32 diversifier derivation
str4d Mar 5, 2021
eaa7158
Use reddsa to instantiate orchard::redpallas
str4d Mar 5, 2021
2750170
Use orchard::redpallas types in orchard::keys implementation
str4d Mar 6, 2021
5772c71
Add doctest example to orchard::Address that exercises key derivation
str4d Mar 6, 2021
a61be5d
Fix typo in documentation
str4d Mar 6, 2021
71542f7
Add internal DiversifiedTransmissionKey type
str4d Mar 6, 2021
9455158
Use protocol spec URL anchors as link handles
str4d Mar 6, 2021
57c6492
Add internal CommitIvkRandomness type
str4d Mar 6, 2021
cfaa61a
Remove unnecessary conversions for DiversifierIndex
str4d Mar 8, 2021
26701c3
Fix commit_ivk specification
str4d Mar 8, 2021
307787e
Use spec name for SpendValidatingKey
str4d Mar 8, 2021
bf5fb7a
Add missing spec links to key docs
str4d Mar 8, 2021
cef44f5
Fix intra-crate doc links
str4d Mar 8, 2021
2462bb2
Use [u8; 64] as the output of prf_expand to match the spec
str4d Mar 8, 2021
f7cad77
Add clarifying note about nomenclature
str4d Mar 8, 2021
e98f324
Ensure diversify_hash does not return the identity
str4d Mar 15, 2021
e0b40cb
FullViewingKey::address_at(impl Into<DiversifierIndex>)
str4d Mar 15, 2021
46bf89c
Update ivk derivation to match latest protocol spec draft
str4d Mar 15, 2021
3c8befa
Remove TODO from extract_p
str4d Mar 15, 2021
8e55b46
Deduplicate default address generation
str4d Mar 15, 2021
e041726
Make address generation infallible again
str4d Mar 17, 2021
42ea809
Update protocol spec references
str4d Mar 17, 2021
861eec1
Document sinsemilla::Pad
str4d Mar 17, 2021
51fd94d
Fix section numbers after spec changes
str4d Mar 18, 2021
05e86a4
Reuse the hasher inside diversify_hash
str4d Mar 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@ publish = false
rustdoc-args = [ "--html-in-header", "katex-header.html" ]

[dependencies]
aes = "0.6"
bitvec = "0.20"
blake2b_simd = "0.5"
ff = "0.9"
fpe = "0.4"
group = "0.9"
halo2 = { git = "https://github.com/zcash/halo2.git", branch = "main" }
nonempty = "0.6"
subtle = "2.3"

[dependencies.reddsa]
git = "https://github.com/str4d/redjubjub.git"
rev = "f8ff124a52d86e122e0705e8e9272f2099fe4c46"

[dev-dependencies]
criterion = "0.3"
Expand Down
19 changes: 17 additions & 2 deletions src/address.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
use crate::keys::Diversifier;
use crate::keys::{DiversifiedTransmissionKey, Diversifier};

/// A shielded payment address.
///
/// # Examples
///
/// ```
/// use orchard::keys::{SpendingKey, FullViewingKey};
///
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
/// let address = FullViewingKey::from(&sk).default_address();
/// ```
#[derive(Debug)]
pub struct Address {
d: Diversifier,
pk_d: (),
pk_d: DiversifiedTransmissionKey,
}

str4d marked this conversation as resolved.
Show resolved Hide resolved
impl Address {
Copy link
Contributor

@dconnolly dconnolly Mar 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An idea: From/Into's alongside the more descriptive type impl methods defined, where the From/Into's call those, so there is no duplication of logic. I push on this because of the orphan rule, otherwise we could just impl all these std traits ourselves: if they aren't defined in here, we'd have to wrap some orchard types to achieve them. :/ I can make code snippet suggestions for them as needed, eg the one above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 this is how I like to write typeclass instances in Haskell, and the analogy to traits is apt: write a function that is typed to whatever you're working on, and then use that function to provide for the generalized dispatch via the trait/typeclass.

pub(crate) fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self {
Address { d, pk_d }
}
}
4 changes: 4 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! Constants used in the Orchard protocol.

/// $\ell^\mathsf{Orchard}_\mathsf{base}$
pub(crate) const L_ORCHARD_BASE: usize = 255;
246 changes: 216 additions & 30 deletions src/keys.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,117 @@
//! Key structures for Orchard.

use crate::address::Address;
use std::convert::TryInto;
use std::mem;

use aes::Aes256;
use fpe::ff1::{BinaryNumeralString, FF1};
use group::GroupEncoding;
use halo2::{arithmetic::FieldExt, pasta::pallas};
use subtle::CtOption;

use crate::{
address::Address,
primitives::redpallas::{self, SpendAuth},
spec::{
commit_ivk, diversify_hash, extract_p, ka_orchard, prf_expand, prf_expand_vec, to_base,
to_scalar,
},
};

/// A spending key, from which all key material is derived.
///
/// TODO: In Sapling we never actually used this, instead deriving everything via ZIP 32,
/// so that we could maintain Bitcoin-like HD keys with properties like non-hardened
/// derivation. If we decide that we don't actually require non-hardened derivation, then
/// we could greatly simplify the HD structure and use this struct directly.
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
daira marked this conversation as resolved.
Show resolved Hide resolved
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub struct SpendingKey;
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())
}
}

/// A spend authorizing key, used to create spend authorization signatures.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub(crate) struct SpendAuthorizingKey;
pub(crate) struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);

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]))
}
}

impl From<&SpendingKey> for SpendAuthorizingKey {
fn from(_: &SpendingKey) -> Self {
todo!()
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.
daira marked this conversation as resolved.
Show resolved Hide resolved
let ret = SpendAuthorizingKey(ask.to_bytes().try_into().unwrap());
// If the last bit of repr_P(ak) is 1, negate ask.
if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 {
str4d marked this conversation as resolved.
Show resolved Hide resolved
SpendAuthorizingKey((-ask).to_bytes().try_into().unwrap())
} else {
ret
}
}
}

/// TODO: This is its protocol spec name for Sapling, but I'd prefer a different name.
/// A key used to validate spend authorization signatures.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
/// Note that this is $\mathsf{ak}^\mathbb{P}$, which by construction is equivalent to
/// $\mathsf{ak}$ but stored here as a RedPallas verification key.
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub(crate) struct AuthorizingKey;
pub(crate) struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);

impl From<&SpendAuthorizingKey> for AuthorizingKey {
fn from(_: &SpendAuthorizingKey) -> Self {
todo!()
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
fn from(ask: &SpendAuthorizingKey) -> Self {
SpendValidatingKey((&ask.0).into())
}
}

/// A key used to derive [`Nullifier`]s from [`Note`]s.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [`Nullifier`]: crate::note::Nullifier
/// [`Note`]: crate::note::Note
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub(crate) struct NullifierDerivingKey;
pub(crate) struct NullifierDerivingKey(pallas::Base);

impl From<&SpendingKey> for NullifierDerivingKey {
fn from(_: &SpendingKey) -> Self {
todo!()
fn from(sk: &SpendingKey) -> Self {
NullifierDerivingKey(to_base(prf_expand(&sk.0, &[0x07])))
}
}

/// 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);
daira marked this conversation as resolved.
Show resolved Hide resolved

impl From<&SpendingKey> for CommitIvkRandomness {
fn from(sk: &SpendingKey) -> Self {
CommitIvkRandomness(to_scalar(prf_expand(&sk.0, &[0x08])))
}
}

Expand All @@ -44,29 +120,111 @@ impl From<&SpendingKey> for NullifierDerivingKey {
/// 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).
///
/// TODO: Should we just define the FVK to include extended stuff like the diversifier key?
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub struct FullViewingKey {
ak: AuthorizingKey,
ak: SpendValidatingKey,
nk: NullifierDerivingKey,
rivk: (),
rivk: CommitIvkRandomness,
}

impl From<&SpendingKey> for FullViewingKey {
fn from(_: &SpendingKey) -> Self {
todo!()
fn from(sk: &SpendingKey) -> Self {
FullViewingKey {
ak: (&SpendAuthorizingKey::from(sk)).into(),
nk: sk.into(),
rivk: sk.into(),
}
}
}

impl FullViewingKey {
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
daira marked this conversation as resolved.
Show resolved Hide resolved
let k = self.rivk.0.to_bytes();
let b = [(&self.ak.0).into(), self.nk.0.to_bytes()];
let r = prf_expand_vec(&k, &[&[0x82], &b[0][..], &b[1][..]]);
(
DiversifierKey(r[..32].try_into().unwrap()),
OutgoingViewingKey(r[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())
}

/// Returns the payment address for this key at the given index.
pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
self.address(DiversifierKey::from(self).get(j))
}

/// Returns the payment address for this key corresponding to the given diversifier.
pub fn address(&self, d: Diversifier) -> Address {
IncomingViewingKey::from(self).address(d)
}
}

/// A key that provides the capability to derive a sequence of diversifiers.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[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 {
self.get(0u32)
}

/// Returns the diversifier at the given index.
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();
daira marked this conversation as resolved.
Show resolved Hide resolved
Diversifier(enc.to_bytes_le().try_into().unwrap())
daira marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// A diversifier that can be used to derive a specific [`Address`] from a
/// [`FullViewingKey`] or [`IncomingViewingKey`].
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub struct Diversifier([u8; 11]);

Expand All @@ -78,19 +236,25 @@ pub struct Diversifier([u8; 11]);
///
/// 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.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub struct IncomingViewingKey;
pub struct IncomingViewingKey(pallas::Scalar);

impl From<&FullViewingKey> for IncomingViewingKey {
fn from(_: &FullViewingKey) -> Self {
todo!()
fn from(fvk: &FullViewingKey) -> Self {
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
IncomingViewingKey(commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0))
}
}

impl IncomingViewingKey {
/// Returns the payment address for this key corresponding to the given diversifier.
pub fn address(&self, _: Diversifier) -> Address {
todo!()
pub fn address(&self, d: Diversifier) -> Address {
str4d marked this conversation as resolved.
Show resolved Hide resolved
let pk_d = DiversifiedTransmissionKey::derive(self, &d);
Address::from_parts(d, pk_d)
}
}

Expand All @@ -99,11 +263,33 @@ impl IncomingViewingKey {
///
/// 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.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub struct OutgoingViewingKey;
pub struct OutgoingViewingKey([u8; 32]);

impl From<&FullViewingKey> for OutgoingViewingKey {
fn from(_: &FullViewingKey) -> Self {
todo!()
fn from(fvk: &FullViewingKey) -> Self {
fvk.derive_dk_ovk().1
}
}

/// The diversified transmission key for a given payment address.
///
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Debug)]
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);

impl DiversifiedTransmissionKey {
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
fn derive(ivk: &IncomingViewingKey, d: &Diversifier) -> Self {
let g_d = diversify_hash(&d.0);
str4d marked this conversation as resolved.
Show resolved Hide resolved
DiversifiedTransmissionKey(ka_orchard(&ivk.0, &g_d))
}
}
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
//! # orchard
//!
//! ## Nomenclature
//!
//! All types in the `orchard` crate, unless otherwise specified, are Orchard-specific
//! types. For example, [`Address`] is documented as being a shielded payment address; we
//! implicitly mean it is an Orchard payment address (as opposed to e.g. a Sapling payment
//! address, which is also shielded).

#![cfg_attr(docsrs, feature(doc_cfg))]
// Catch documentation errors caused by code changes.
Expand All @@ -10,9 +17,11 @@
mod address;
pub mod bundle;
mod circuit;
mod constants;
pub mod keys;
mod note;
pub mod primitives;
mod spec;
daira marked this conversation as resolved.
Show resolved Hide resolved
mod tree;
pub mod value;

Expand Down
Loading