-
Notifications
You must be signed in to change notification settings - Fork 347
Sapling note encryption #69
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
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
9b455a1
Move Sapling key structs from zip32 to zcash_primitives
str4d 65bbe7d
Implement Sapling note encryption
gtank 70caa7d
Sapling note encryption test vectors
str4d 757316d
Test prf_ock
str4d 6996853
Trial Sapling note decryption
str4d 8e098d4
Trial Sapling output recovery
str4d 484330e
Trial Sapling compact note decryption
str4d 247f3fb
Impl traits and functions for Memo
str4d 2b1583d
Deduplicate Sapling key agreement logic
str4d 566db65
Use a slice instead of a vector in prf_expand()
str4d 899d852
Inline empty nonces
str4d 9086dd9
Enforce consistent plaintext and ciphertext lengths
str4d e17e4b1
Test invalid decryption edge cases
str4d 6dcb404
Switch to crypto_api_chachapoly crate
str4d 34658c4
Raise minimum Rust version to 1.32
str4d edf7bc1
Document note_encryption module
str4d 6d03b5c
Replace AeadCipher::seal with AeadCipher::seal_to
str4d 23aa869
Add comments with specification references
str4d d4fce58
Tweaks to debug output and function names
str4d 75bede4
Use fixed-length arrays instead of Vec
str4d 6846ac5
Require that ak in FullViewingKey is prime order
str4d 9d80be6
Simplify Memo::to_utf8 implementation
str4d 060977f
Return edwards::Point from sapling_ka_agree
str4d fdb6e20
Check note plaintext version byte when decrypting
str4d b65aae9
Test both invalid and incorrect diversifiers
str4d File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,6 +1,6 @@ | ||
| language: rust | ||
| rust: | ||
| - 1.31.0 | ||
| - 1.32.0 | ||
|
|
||
| cache: cargo | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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,218 @@ | ||
| //! Sapling key components. | ||
| //! | ||
| //! Implements section 4.2.2 of the Zcash Protocol Specification. | ||
|
|
||
| use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; | ||
| use ff::{PrimeField, PrimeFieldRepr}; | ||
| use sapling_crypto::{ | ||
| jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown}, | ||
| primitives::{ProofGenerationKey, ViewingKey}, | ||
| }; | ||
| use std::io::{self, Read, Write}; | ||
|
|
||
| pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed"; | ||
|
|
||
| /// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) | ||
| pub fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bResult { | ||
| prf_expand_vec(sk, &[t]) | ||
| } | ||
|
|
||
| pub fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bResult { | ||
| let mut h = Blake2b::with_params(64, &[], &[], PRF_EXPAND_PERSONALIZATION); | ||
| h.update(sk); | ||
| for t in ts { | ||
| h.update(t); | ||
| } | ||
| h.finalize() | ||
| } | ||
|
|
||
| /// An outgoing viewing key | ||
| #[derive(Clone, Copy, Debug, PartialEq)] | ||
| pub struct OutgoingViewingKey(pub [u8; 32]); | ||
|
|
||
| /// A Sapling expanded spending key | ||
| #[derive(Clone)] | ||
| pub struct ExpandedSpendingKey<E: JubjubEngine> { | ||
| pub ask: E::Fs, | ||
| pub nsk: E::Fs, | ||
| pub ovk: OutgoingViewingKey, | ||
| } | ||
|
|
||
| /// A Sapling full viewing key | ||
| #[derive(Debug)] | ||
| pub struct FullViewingKey<E: JubjubEngine> { | ||
| pub vk: ViewingKey<E>, | ||
| pub ovk: OutgoingViewingKey, | ||
| } | ||
|
|
||
| impl<E: JubjubEngine> ExpandedSpendingKey<E> { | ||
| pub fn from_spending_key(sk: &[u8]) -> Self { | ||
| let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes()); | ||
| let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes()); | ||
| let mut ovk = OutgoingViewingKey([0u8; 32]); | ||
| ovk.0 | ||
| .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); | ||
| ExpandedSpendingKey { ask, nsk, ovk } | ||
| } | ||
|
|
||
| pub fn proof_generation_key(&self, params: &E::Params) -> ProofGenerationKey<E> { | ||
| ProofGenerationKey { | ||
| ak: params | ||
| .generator(FixedGenerators::SpendingKeyGenerator) | ||
| .mul(self.ask, params), | ||
| nsk: self.nsk, | ||
| } | ||
| } | ||
|
|
||
| pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { | ||
| let mut ask_repr = <E::Fs as PrimeField>::Repr::default(); | ||
| ask_repr.read_le(&mut reader)?; | ||
| let ask = E::Fs::from_repr(ask_repr) | ||
| .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; | ||
|
|
||
| let mut nsk_repr = <E::Fs as PrimeField>::Repr::default(); | ||
| nsk_repr.read_le(&mut reader)?; | ||
| let nsk = E::Fs::from_repr(nsk_repr) | ||
| .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; | ||
|
|
||
| let mut ovk = [0; 32]; | ||
| reader.read_exact(&mut ovk)?; | ||
|
|
||
| Ok(ExpandedSpendingKey { | ||
| ask, | ||
| nsk, | ||
| ovk: OutgoingViewingKey(ovk), | ||
| }) | ||
| } | ||
|
|
||
| pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { | ||
| self.ask.into_repr().write_le(&mut writer)?; | ||
| self.nsk.into_repr().write_le(&mut writer)?; | ||
| writer.write_all(&self.ovk.0)?; | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn to_bytes(&self) -> [u8; 96] { | ||
| let mut result = [0u8; 96]; | ||
| self.write(&mut result[..]) | ||
| .expect("should be able to serialize an ExpandedSpendingKey"); | ||
| result | ||
| } | ||
| } | ||
|
|
||
| impl<E: JubjubEngine> Clone for FullViewingKey<E> { | ||
| fn clone(&self) -> Self { | ||
| FullViewingKey { | ||
| vk: ViewingKey { | ||
| ak: self.vk.ak.clone(), | ||
| nk: self.vk.nk.clone(), | ||
| }, | ||
| ovk: self.ovk.clone(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl<E: JubjubEngine> FullViewingKey<E> { | ||
| pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey<E>, params: &E::Params) -> Self { | ||
| FullViewingKey { | ||
| vk: ViewingKey { | ||
| ak: params | ||
| .generator(FixedGenerators::SpendingKeyGenerator) | ||
| .mul(expsk.ask, params), | ||
| nk: params | ||
| .generator(FixedGenerators::ProofGenerationKey) | ||
| .mul(expsk.nsk, params), | ||
| }, | ||
| ovk: expsk.ovk, | ||
| } | ||
| } | ||
|
|
||
| pub fn read<R: Read>(mut reader: R, params: &E::Params) -> io::Result<Self> { | ||
| let ak = edwards::Point::<E, Unknown>::read(&mut reader, params)?; | ||
| let ak = match ak.as_prime_order(params) { | ||
| Some(p) => p, | ||
| None => { | ||
| return Err(io::Error::new( | ||
| io::ErrorKind::InvalidData, | ||
| "ak not in prime-order subgroup", | ||
| )); | ||
| } | ||
| }; | ||
| if ak == edwards::Point::zero() { | ||
| return Err(io::Error::new( | ||
| io::ErrorKind::InvalidData, | ||
| "ak not of prime order", | ||
| )); | ||
| } | ||
|
|
||
| let nk = edwards::Point::<E, Unknown>::read(&mut reader, params)?; | ||
| let nk = match nk.as_prime_order(params) { | ||
|
daira marked this conversation as resolved.
|
||
| Some(p) => p, | ||
| None => { | ||
| return Err(io::Error::new( | ||
| io::ErrorKind::InvalidData, | ||
| "nk not in prime-order subgroup", | ||
| )); | ||
| } | ||
| }; | ||
|
|
||
| let mut ovk = [0; 32]; | ||
| reader.read_exact(&mut ovk)?; | ||
|
|
||
| Ok(FullViewingKey { | ||
| vk: ViewingKey { ak, nk }, | ||
| ovk: OutgoingViewingKey(ovk), | ||
| }) | ||
| } | ||
|
|
||
| pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { | ||
| self.vk.ak.write(&mut writer)?; | ||
| self.vk.nk.write(&mut writer)?; | ||
| writer.write_all(&self.ovk.0)?; | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn to_bytes(&self) -> [u8; 96] { | ||
| let mut result = [0u8; 96]; | ||
| self.write(&mut result[..]) | ||
| .expect("should be able to serialize a FullViewingKey"); | ||
| result | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use pairing::bls12_381::Bls12; | ||
| use sapling_crypto::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder}; | ||
| use std::error::Error; | ||
|
|
||
| use super::FullViewingKey; | ||
| use crate::JUBJUB; | ||
|
|
||
| #[test] | ||
| fn ak_must_be_prime_order() { | ||
| let mut buf = [0; 96]; | ||
| let identity = edwards::Point::<Bls12, PrimeOrder>::zero(); | ||
|
|
||
| // Set both ak and nk to the identity. | ||
| identity.write(&mut buf[0..32]).unwrap(); | ||
| identity.write(&mut buf[32..64]).unwrap(); | ||
|
|
||
| // ak is not allowed to be the identity. | ||
| assert_eq!( | ||
| FullViewingKey::<Bls12>::read(&buf[..], &JUBJUB) | ||
| .unwrap_err() | ||
| .description(), | ||
| "ak not of prime order" | ||
| ); | ||
|
|
||
| // Set ak to a basepoint. | ||
| let basepoint = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator); | ||
| basepoint.write(&mut buf[0..32]).unwrap(); | ||
|
|
||
| // nk is allowed to be the identity. | ||
| assert!(FullViewingKey::<Bls12>::read(&buf[..], &JUBJUB).is_ok()); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.