Browse Source

Document note_encryption module

master
Jack Grigg 6 years ago
parent
commit
edf7bc144d
No known key found for this signature in database
GPG Key ID: 9E8255172BBF9898
  1. 92
      zcash_primitives/src/note_encryption.rs

92
zcash_primitives/src/note_encryption.rs

@ -1,3 +1,5 @@
//! Implementation of in-band secret distribution for Zcash transactions.
use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; use blake2_rfc::blake2b::{Blake2b, Blake2bResult};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
@ -57,6 +59,7 @@ where
Ok(()) Ok(())
} }
/// An unencrypted memo received alongside a shielded note in a Zcash transaction.
#[derive(Clone)] #[derive(Clone)]
pub struct Memo([u8; 512]); pub struct Memo([u8; 512]);
@ -87,9 +90,9 @@ impl PartialEq for Memo {
} }
impl Memo { impl Memo {
/// Returns a Memo containing the given slice, appending with zero bytes if necessary, /// Returns a `Memo` containing the given slice, appending with zero bytes if
/// or None if the slice is too long. If the slice is empty, Memo::default() is /// necessary, or `None` if the slice is too long. If the slice is empty,
/// returned. /// `Memo::default` is returned.
pub fn from_bytes(memo: &[u8]) -> Option<Memo> { pub fn from_bytes(memo: &[u8]) -> Option<Memo> {
if memo.is_empty() { if memo.is_empty() {
Some(Memo::default()) Some(Memo::default())
@ -103,19 +106,20 @@ impl Memo {
} }
} }
/// Returns a Memo containing the given string, or None if the string is too long. /// Returns a `Memo` containing the given string, or `None` if the string is too long.
pub fn from_str(memo: &str) -> Option<Memo> { pub fn from_str(memo: &str) -> Option<Memo> {
Memo::from_bytes(memo.as_bytes()) Memo::from_bytes(memo.as_bytes())
} }
/// Returns the underlying bytes of the `Memo`.
pub fn as_bytes(&self) -> &[u8] { pub fn as_bytes(&self) -> &[u8] {
&self.0[..] &self.0[..]
} }
/// Returns: /// Returns:
/// - None if the memo is not text /// - `None` if the memo is not text
/// - Some(Ok(memo)) if the memo contains a valid UTF8 string /// - `Some(Ok(memo))` if the memo contains a valid UTF-8 string
/// - Some(Err(e)) if the memo contains invalid UTF8 /// - `Some(Err(e))` if the memo contains invalid UTF-8
pub fn to_utf8(&self) -> Option<Result<String, str::Utf8Error>> { pub fn to_utf8(&self) -> Option<Result<String, str::Utf8Error>> {
// Check if it is a text or binary memo // Check if it is a text or binary memo
if self.0[0] < 0xF5 { if self.0[0] < 0xF5 {
@ -144,6 +148,9 @@ fn generate_esk() -> Fs {
Fs::to_uniform(&buffer[..]) Fs::to_uniform(&buffer[..])
} }
/// Sapling key agreement for note encryption.
///
/// Implements section 5.4.4.3 of the Zcash Protocol Specification.
pub fn sapling_ka_agree<'a, P>(esk: &Fs, pk_d: &'a P) -> [u8; 32] pub fn sapling_ka_agree<'a, P>(esk: &Fs, pk_d: &'a P) -> [u8; 32]
where where
edwards::Point<Bls12, Unknown>: From<&'a P>, edwards::Point<Bls12, Unknown>: From<&'a P>,
@ -162,6 +169,9 @@ where
result result
} }
/// Sapling KDF for note encryption.
///
/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
fn kdf_sapling(dhsecret: &[u8], epk: &edwards::Point<Bls12, PrimeOrder>) -> Blake2bResult { fn kdf_sapling(dhsecret: &[u8], epk: &edwards::Point<Bls12, PrimeOrder>) -> Blake2bResult {
let mut input = [0u8; 64]; let mut input = [0u8; 64];
input[0..32].copy_from_slice(&dhsecret); input[0..32].copy_from_slice(&dhsecret);
@ -172,6 +182,9 @@ fn kdf_sapling(dhsecret: &[u8], epk: &edwards::Point<Bls12, PrimeOrder>) -> Blak
h.finalize() h.finalize()
} }
/// Sapling PRF^ock.
///
/// Implemented per section 5.4.2 of the Zcash Protocol Specification.
fn prf_ock( fn prf_ock(
ovk: &OutgoingViewingKey, ovk: &OutgoingViewingKey,
cv: &edwards::Point<Bls12, Unknown>, cv: &edwards::Point<Bls12, Unknown>,
@ -189,6 +202,56 @@ fn prf_ock(
h.finalize() h.finalize()
} }
/// An API for encrypting Sapling notes.
///
/// This struct provides a safe API for encrypting Sapling notes. In particular, it
/// enforces that fresh ephemeral keys are used for every note, and that the ciphertexts
/// are consistent with each other.
///
/// Implements section 4.17.1 of the Zcash Protocol Specification.
///
/// # Examples
///
/// ```
/// extern crate pairing;
/// extern crate rand;
/// extern crate sapling_crypto;
///
/// use pairing::bls12_381::Bls12;
/// use rand::{OsRng, Rand};
/// use sapling_crypto::{
/// jubjub::fs::Fs,
/// primitives::{Diversifier, PaymentAddress, ValueCommitment},
/// };
/// use zcash_primitives::{
/// keys::OutgoingViewingKey,
/// note_encryption::{Memo, SaplingNoteEncryption},
/// JUBJUB,
/// };
///
/// let mut rng = OsRng::new().unwrap();
///
/// let diversifier = Diversifier([0; 11]);
/// let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap();
/// let to = PaymentAddress {
/// pk_d,
/// diversifier,
/// };
/// let ovk = OutgoingViewingKey([0; 32]);
///
/// let value = 1000;
/// let rcv = Fs::rand(&mut rng);
/// let cv = ValueCommitment::<Bls12> {
/// value,
/// randomness: rcv.clone(),
/// };
/// let note = to.create_note(value, rcv, &JUBJUB).unwrap();
/// let cmu = note.cm(&JUBJUB);
///
/// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default());
/// let encCiphertext = enc.encrypt_note_plaintext();
/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm(&JUBJUB).into(), &cmu);
/// ```
pub struct SaplingNoteEncryption { pub struct SaplingNoteEncryption {
epk: edwards::Point<Bls12, PrimeOrder>, epk: edwards::Point<Bls12, PrimeOrder>,
esk: Fs, esk: Fs,
@ -199,6 +262,7 @@ pub struct SaplingNoteEncryption {
} }
impl SaplingNoteEncryption { impl SaplingNoteEncryption {
/// Creates a new encryption context for the given note.
pub fn new( pub fn new(
ovk: OutgoingViewingKey, ovk: OutgoingViewingKey,
note: Note<Bls12>, note: Note<Bls12>,
@ -218,14 +282,17 @@ impl SaplingNoteEncryption {
} }
} }
/// Exposes the ephemeral secret key being used to encrypt this note.
pub fn esk(&self) -> &Fs { pub fn esk(&self) -> &Fs {
&self.esk &self.esk
} }
/// Exposes the ephemeral public key being used to encrypt this note.
pub fn epk(&self) -> &edwards::Point<Bls12, PrimeOrder> { pub fn epk(&self) -> &edwards::Point<Bls12, PrimeOrder> {
&self.epk &self.epk
} }
/// Generates `encCiphertext` for this note.
pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] { pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] {
let shared_secret = sapling_ka_agree(&self.esk, &self.to.pk_d); let shared_secret = sapling_ka_agree(&self.esk, &self.to.pk_d);
let key = kdf_sapling(&shared_secret, &self.epk); let key = kdf_sapling(&shared_secret, &self.epk);
@ -251,6 +318,7 @@ impl SaplingNoteEncryption {
output output
} }
/// Generates `outCiphertext` for this note.
pub fn encrypt_outgoing_plaintext( pub fn encrypt_outgoing_plaintext(
&self, &self,
cv: &edwards::Point<Bls12, Unknown>, cv: &edwards::Point<Bls12, Unknown>,
@ -311,6 +379,8 @@ fn parse_note_plaintext_minus_memo(
Some((note, to)) Some((note, to))
} }
/// Trial decryption of the full note plaintext by the recipient.
///
/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ivk`. /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ivk`.
/// If successful, the corresponding Sapling note and memo are returned, along with the /// If successful, the corresponding Sapling note and memo are returned, along with the
/// `PaymentAddress` to which the note was sent. /// `PaymentAddress` to which the note was sent.
@ -349,11 +419,15 @@ pub fn try_sapling_note_decryption(
Some((note, to, Memo(memo))) Some((note, to, Memo(memo)))
} }
/// Trial decryption of the compact note plaintext by the recipient for light clients.
///
/// Attempts to decrypt and validate the first 52 bytes of `enc_ciphertext` using the /// Attempts to decrypt and validate the first 52 bytes of `enc_ciphertext` using the
/// given `ivk`. If successful, the corresponding Sapling note is returned, along with the /// given `ivk`. If successful, the corresponding Sapling note is returned, along with the
/// `PaymentAddress` to which the note was sent. /// `PaymentAddress` to which the note was sent.
/// ///
/// Implements the procedure specified in ZIP 307. /// Implements the procedure specified in [`ZIP 307`].
///
/// [`ZIP 307`]: https://github.com/zcash/zips/pull/226
pub fn try_sapling_compact_note_decryption( pub fn try_sapling_compact_note_decryption(
ivk: &Fs, ivk: &Fs,
epk: &edwards::Point<Bls12, PrimeOrder>, epk: &edwards::Point<Bls12, PrimeOrder>,
@ -385,6 +459,8 @@ pub fn try_sapling_compact_note_decryption(
parse_note_plaintext_minus_memo(ivk, cmu, &plaintext[CHACHA20_BLOCK_SIZE..]) parse_note_plaintext_minus_memo(ivk, cmu, &plaintext[CHACHA20_BLOCK_SIZE..])
} }
/// Recovery of the full note plaintext by the sender.
///
/// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`.
/// If successful, the corresponding Sapling note and memo are returned, along with the /// If successful, the corresponding Sapling note and memo are returned, along with the
/// `PaymentAddress` to which the note was sent. /// `PaymentAddress` to which the note was sent.

Loading…
Cancel
Save