-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from zcash/prf_expand
Introduce type-safe `PRF^expand`
- Loading branch information
Showing
4 changed files
with
145 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
#![deny(rustdoc::broken_intra_doc_links)] | ||
|
||
mod prf_expand; | ||
pub use prf_expand::PrfExpand; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use std::marker::PhantomData; | ||
|
||
use blake2b_simd::Params; | ||
|
||
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; | ||
|
||
/// The set of domains in which $PRF^\mathsf{expand}$ is defined. | ||
/// | ||
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. | ||
/// | ||
/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs | ||
pub struct PrfExpand<T> { | ||
domain_separator: u8, | ||
_input: PhantomData<T>, | ||
} | ||
|
||
impl<T> PrfExpand<T> { | ||
/// Defines a new $PRF^\mathsf{expand}$ domain. | ||
/// | ||
/// Private because we want to ensure that all domains are defined in the same place, | ||
/// to avoid bugs where a domain separator is accidentally reused. | ||
const fn new(domain_separator: u8) -> Self { | ||
Self { | ||
domain_separator, | ||
_input: PhantomData, | ||
} | ||
} | ||
|
||
/// Expands the given secret key in this domain, with additional data concatenated | ||
/// from the given slices. | ||
/// | ||
/// $PRF^\mathsf{expand}(sk, dst, a, b, ...) := BLAKE2b-512("Zcash_ExpandSeed", sk || dst || a || b || ...)$ | ||
/// | ||
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. | ||
/// | ||
/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs | ||
fn apply(self, sk: &[u8], ts: &[&[u8]]) -> [u8; 64] { | ||
let mut h = Params::new() | ||
.hash_length(64) | ||
.personal(PRF_EXPAND_PERSONALIZATION) | ||
.to_state(); | ||
h.update(sk); | ||
h.update(&[self.domain_separator]); | ||
for t in ts { | ||
h.update(t); | ||
} | ||
*h.finalize().as_array() | ||
} | ||
} | ||
|
||
macro_rules! with_inputs { | ||
($($arr:ident, $arrlen:ident),*) => { | ||
#[allow(unused_parens)] | ||
impl<$(const $arrlen: usize),*> PrfExpand<($([u8; $arrlen]),*)> { | ||
/// Expands the given secret key in this domain. | ||
pub fn with(self, sk: &[u8], $($arr: &[u8; $arrlen]),*) -> [u8; 64] { | ||
self.apply(sk, &[$($arr),*]) | ||
} | ||
} | ||
}; | ||
} | ||
|
||
impl PrfExpand<()> { | ||
pub const SAPLING_ASK: Self = Self::new(0x00); | ||
pub const SAPLING_NSK: Self = Self::new(0x01); | ||
pub const SAPLING_OVK: Self = Self::new(0x02); | ||
pub const SAPLING_RCM: Self = Self::new(0x04); | ||
pub const SAPLING_ESK: Self = Self::new(0x05); | ||
pub const ORCHARD_ASK: Self = Self::new(0x06); | ||
pub const ORCHARD_NK: Self = Self::new(0x07); | ||
pub const ORCHARD_RIVK: Self = Self::new(0x08); | ||
pub const SAPLING_ZIP32_MASTER_DK: Self = Self::new(0x10); | ||
pub const SAPLING_ZIP32_CHILD_I_ASK: Self = Self::new(0x13); | ||
pub const SAPLING_ZIP32_CHILD_I_NSK: Self = Self::new(0x14); | ||
pub const SAPLING_ZIP32_INTERNAL_NSK: Self = Self::new(0x17); | ||
pub const SAPLING_ZIP32_INTERNAL_DK_OVK: Self = Self::new(0x18); | ||
} | ||
with_inputs!(); | ||
|
||
impl PrfExpand<[u8; 1]> { | ||
pub const SAPLING_DEFAULT_DIVERSIFIER: Self = Self::new(0x03); | ||
} | ||
impl PrfExpand<[u8; 32]> { | ||
pub const ORCHARD_ESK: Self = Self::new(0x04); | ||
pub const ORCHARD_RCM: Self = Self::new(0x05); | ||
pub const PSI: Self = Self::new(0x09); | ||
pub const SAPLING_ZIP32_CHILD_OVK: Self = Self::new(0x15); | ||
pub const SAPLING_ZIP32_CHILD_DK: Self = Self::new(0x16); | ||
} | ||
impl PrfExpand<[u8; 33]> { | ||
pub const TRANSPARENT_ZIP316_OVK: Self = Self::new(0xD0); | ||
} | ||
with_inputs!(a, A); | ||
|
||
impl PrfExpand<([u8; 32], [u8; 4])> { | ||
pub const SPROUT_ZIP32_CHILD: Self = Self::new(0x80); | ||
pub const ORCHARD_ZIP32_CHILD: Self = Self::new(0x81); | ||
} | ||
impl PrfExpand<([u8; 32], [u8; 32])> { | ||
pub const ORCHARD_DK_OVK: Self = Self::new(0x82); | ||
pub const ORCHARD_RIVK_INTERNAL: Self = Self::new(0x83); | ||
} | ||
with_inputs!(a, A, b, B); | ||
|
||
impl PrfExpand<([u8; 96], [u8; 32], [u8; 4])> { | ||
pub const SAPLING_ZIP32_CHILD_HARDENED: Self = Self::new(0x11); | ||
pub const SAPLING_ZIP32_CHILD_NON_HARDENED: Self = Self::new(0x12); | ||
} | ||
with_inputs!(a, A, b, B, c, C); |