Merge pull request #475 from zcash/sapling-internal-keys
Derive Sapling internal keys.
This commit is contained in:
commit
40377dd9ab
|
@ -31,8 +31,8 @@ and this library adheres to Rust's notion of
|
||||||
Orchard). This type makes it possible to encode a type-safe state machine
|
Orchard). This type makes it possible to encode a type-safe state machine
|
||||||
for the application of authorizing data to a transaction; implementations of
|
for the application of authorizing data to a transaction; implementations of
|
||||||
this trait represent different states of the authorization process.
|
this trait represent different states of the authorization process.
|
||||||
- New bundle types under the `zcash_primitives::transaction` submodules, one for
|
- New bundle types under the `zcash_primitives::transaction` submodules, one for
|
||||||
each Zcash sub-protocol. These are now used instead of bare fields
|
each Zcash sub-protocol. These are now used instead of bare fields
|
||||||
within the `TransactionData` type.
|
within the `TransactionData` type.
|
||||||
- `components::sapling::Bundle` bundle of
|
- `components::sapling::Bundle` bundle of
|
||||||
Sapling transaction elements. This new struct is parameterized by a
|
Sapling transaction elements. This new struct is parameterized by a
|
||||||
|
@ -58,7 +58,7 @@ and this library adheres to Rust's notion of
|
||||||
diversifier index space, whereas `sapling_diversifier` just attempts to use the
|
diversifier index space, whereas `sapling_diversifier` just attempts to use the
|
||||||
provided diversifier index and returns `None` if it does not produce a valid
|
provided diversifier index and returns `None` if it does not produce a valid
|
||||||
diversifier.
|
diversifier.
|
||||||
- `zcash_primitives::zip32::DiversifierKey::diversifier` has been renamed to
|
- `zcash_primitives::zip32::DiversifierKey::diversifier` has been renamed to
|
||||||
`find_diversifier` and the `diversifier` method has new semantics.
|
`find_diversifier` and the `diversifier` method has new semantics.
|
||||||
`find_diversifier` searches the diversifier index space to find a diversifier
|
`find_diversifier` searches the diversifier index space to find a diversifier
|
||||||
index which produces a valid diversifier, whereas `diversifier` just attempts
|
index which produces a valid diversifier, whereas `diversifier` just attempts
|
||||||
|
@ -71,6 +71,23 @@ and this library adheres to Rust's notion of
|
||||||
just attempts to create an address corresponding to the diversifier derived
|
just attempts to create an address corresponding to the diversifier derived
|
||||||
from the provided diversifier index and returns `None` if the provided index
|
from the provided diversifier index and returns `None` if the provided index
|
||||||
does not produce a valid diversifier.
|
does not produce a valid diversifier.
|
||||||
|
- `zcash_primitives::zip32::ExtendedSpendingKey.derive_internal` has been
|
||||||
|
added to facilitate the derivation of an internal (change) spending key.
|
||||||
|
This spending key can be used to spend change sent to an internal address
|
||||||
|
corresponding to the associated full viewing key as specified in
|
||||||
|
[ZIP 316](https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys)..
|
||||||
|
- `zcash_primitives::zip32::ExtendedFullViewingKey.derive_internal` has been
|
||||||
|
added to facilitate the derivation of an internal (change) spending key.
|
||||||
|
This spending key can be used to spend change sent to an internal address
|
||||||
|
corresponding to the associated full viewing key as specified in
|
||||||
|
[ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key).
|
||||||
|
- `zcash_primitives::zip32::sapling_derive_internal_fvk` provides the
|
||||||
|
internal implementation of `ExtendedFullViewingKey.derive_internal`
|
||||||
|
but does not require a complete extended full viewing key, just
|
||||||
|
the full viewing key and the diversifier key. In the future, this
|
||||||
|
function will likely be refactored to become a member function of
|
||||||
|
a new `DiversifiableFullViewingKey` type, which represents the ability
|
||||||
|
to derive IVKs, OVKs, and addresses, but not child viewing keys.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- MSRV is now 1.51.0.
|
- MSRV is now 1.51.0.
|
||||||
|
|
|
@ -6,6 +6,7 @@ use aes::Aes256;
|
||||||
use blake2b_simd::Params as Blake2bParams;
|
use blake2b_simd::Params as Blake2bParams;
|
||||||
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use fpe::ff1::{BinaryNumeralString, FF1};
|
use fpe::ff1::{BinaryNumeralString, FF1};
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::ops::AddAssign;
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -20,6 +21,7 @@ use crate::sapling::keys::{
|
||||||
|
|
||||||
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
|
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
|
||||||
pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP";
|
pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP";
|
||||||
|
pub const ZIP32_SAPLING_INT_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingInt";
|
||||||
|
|
||||||
// Common helper functions
|
// Common helper functions
|
||||||
|
|
||||||
|
@ -233,6 +235,44 @@ pub fn sapling_default_address(
|
||||||
sapling_find_address(fvk, dk, DiversifierIndex::new()).unwrap()
|
sapling_find_address(fvk, dk, DiversifierIndex::new()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the internal full viewing key and diversifier key
|
||||||
|
/// for the provided external FVK = (ak, nk, ovk) and dk encoded
|
||||||
|
/// in a [Unified FVK].
|
||||||
|
///
|
||||||
|
/// [Unified FVK]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
|
||||||
|
pub fn sapling_derive_internal_fvk(
|
||||||
|
fvk: &FullViewingKey,
|
||||||
|
dk: &DiversifierKey,
|
||||||
|
) -> (FullViewingKey, DiversifierKey) {
|
||||||
|
let i = {
|
||||||
|
let mut h = Blake2bParams::new()
|
||||||
|
.hash_length(32)
|
||||||
|
.personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION)
|
||||||
|
.to_state();
|
||||||
|
h.update(&fvk.to_bytes());
|
||||||
|
h.update(&dk.0);
|
||||||
|
h.finalize()
|
||||||
|
};
|
||||||
|
let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array());
|
||||||
|
let r = prf_expand(i.as_bytes(), &[0x18]);
|
||||||
|
let r = r.as_bytes();
|
||||||
|
// PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling
|
||||||
|
let nk_internal = PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk;
|
||||||
|
let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
|
||||||
|
let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
|
||||||
|
|
||||||
|
(
|
||||||
|
FullViewingKey {
|
||||||
|
vk: ViewingKey {
|
||||||
|
ak: fvk.vk.ak,
|
||||||
|
nk: nk_internal,
|
||||||
|
},
|
||||||
|
ovk: ovk_internal,
|
||||||
|
},
|
||||||
|
dk_internal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// A Sapling extended spending key
|
/// A Sapling extended spending key
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExtendedSpendingKey {
|
pub struct ExtendedSpendingKey {
|
||||||
|
@ -409,6 +449,41 @@ impl ExtendedSpendingKey {
|
||||||
pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
|
pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
|
||||||
ExtendedFullViewingKey::from(self).default_address()
|
ExtendedFullViewingKey::from(self).default_address()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derives an internal spending key given an external spending key.
|
||||||
|
///
|
||||||
|
/// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key).
|
||||||
|
pub fn derive_internal(&self) -> Self {
|
||||||
|
let i = {
|
||||||
|
let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
|
||||||
|
let mut h = Blake2bParams::new()
|
||||||
|
.hash_length(32)
|
||||||
|
.personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION)
|
||||||
|
.to_state();
|
||||||
|
h.update(&fvk.to_bytes());
|
||||||
|
h.update(&self.dk.0);
|
||||||
|
h.finalize()
|
||||||
|
};
|
||||||
|
let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array());
|
||||||
|
let r = prf_expand(i.as_bytes(), &[0x18]);
|
||||||
|
let r = r.as_bytes();
|
||||||
|
let nsk_internal = i_nsk + self.expsk.nsk;
|
||||||
|
let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
|
||||||
|
let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
|
||||||
|
|
||||||
|
ExtendedSpendingKey {
|
||||||
|
depth: self.depth,
|
||||||
|
parent_fvk_tag: self.parent_fvk_tag,
|
||||||
|
child_index: self.child_index,
|
||||||
|
chain_code: self.chain_code,
|
||||||
|
expsk: ExpandedSpendingKey {
|
||||||
|
ask: self.expsk.ask,
|
||||||
|
nsk: nsk_internal,
|
||||||
|
ovk: ovk_internal,
|
||||||
|
},
|
||||||
|
dk: dk_internal,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey {
|
impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey {
|
||||||
|
@ -513,6 +588,25 @@ impl ExtendedFullViewingKey {
|
||||||
pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
|
pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
|
||||||
sapling_default_address(&self.fvk, &self.dk)
|
sapling_default_address(&self.fvk, &self.dk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Derives an internal full viewing key used for internal operations such
|
||||||
|
/// as change and auto-shielding. The internal FVK has the same spend authority
|
||||||
|
/// (the private key corresponding to ak) as the original, but viewing authority
|
||||||
|
/// only for internal transfers.
|
||||||
|
///
|
||||||
|
/// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-full-viewing-key).
|
||||||
|
pub fn derive_internal(&self) -> Self {
|
||||||
|
let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&self.fvk, &self.dk);
|
||||||
|
|
||||||
|
ExtendedFullViewingKey {
|
||||||
|
depth: self.depth,
|
||||||
|
parent_fvk_tag: self.parent_fvk_tag,
|
||||||
|
child_index: self.child_index,
|
||||||
|
chain_code: self.chain_code,
|
||||||
|
fvk: fvk_internal,
|
||||||
|
dk: dk_internal,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in New Issue