From 39996300519fac30f160b0dc0f60e4646d326648 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 10 Jun 2021 18:35:19 +0100 Subject: [PATCH] zcash_note_encryption: Add `ShieldedOutput::ephemeral_key() -> EphemeralKeyBytes` This replaces the `ShieldedOutput::epk() -> &Domain::EphemeralPublicKey` which could not be satisfied by output types that did not parse epk. Extracted from: https://github.com/zcash/librustzcash/commit/c7c79d266efe626eaa67e0997b3a3ca97dd2552e --- src/lib.rs | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5c4ccbb..6131cda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,6 +121,8 @@ pub trait Domain { fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes; + fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option; + fn check_epk_bytes NoteValidity>( note: &Self::Note, check: F, @@ -138,7 +140,7 @@ pub trait Domain { &self, pk_d: &Self::DiversifiedTransmissionKey, esk: &Self::EphemeralSecretKey, - epk: &Self::EphemeralPublicKey, + ephemeral_key: &EphemeralKeyBytes, plaintext: &[u8], ) -> Option<(Self::Note, Self::Recipient)>; @@ -155,7 +157,7 @@ pub trait Domain { } pub trait ShieldedOutput { - fn epk(&self) -> &D::EphemeralPublicKey; + fn ephemeral_key(&self) -> EphemeralKeyBytes; fn cmstar_bytes(&self) -> D::ExtractedCommitmentBytes; fn enc_ciphertext(&self) -> &[u8]; } @@ -332,9 +334,11 @@ pub fn try_note_decryption>( output: &Output, ) -> Option<(D::Note, D::Recipient, D::Memo)> { assert_eq!(output.enc_ciphertext().len(), ENC_CIPHERTEXT_SIZE); + let ephemeral_key = output.ephemeral_key(); - let shared_secret = D::ka_agree_dec(ivk, output.epk()); - let key = D::kdf(shared_secret, &D::epk_bytes(output.epk())); + let epk = D::epk(&ephemeral_key)?; + let shared_secret = D::ka_agree_dec(ivk, &epk); + let key = D::kdf(shared_secret, &ephemeral_key); let mut plaintext = [0; ENC_CIPHERTEXT_SIZE]; assert_eq!( @@ -353,7 +357,7 @@ pub fn try_note_decryption>( let (note, to) = parse_note_plaintext_without_memo_ivk( domain, ivk, - output.epk(), + &ephemeral_key, &output.cmstar_bytes(), &plaintext, )?; @@ -365,13 +369,13 @@ pub fn try_note_decryption>( fn parse_note_plaintext_without_memo_ivk( domain: &D, ivk: &D::IncomingViewingKey, - epk: &D::EphemeralPublicKey, + ephemeral_key: &EphemeralKeyBytes, cmstar_bytes: &D::ExtractedCommitmentBytes, plaintext: &[u8], ) -> Option<(D::Note, D::Recipient)> { let (note, to) = domain.parse_note_plaintext_without_memo_ivk(ivk, &plaintext)?; - if let NoteValidity::Valid = check_note_validity::(¬e, epk, cmstar_bytes) { + if let NoteValidity::Valid = check_note_validity::(¬e, ephemeral_key, cmstar_bytes) { Some((note, to)) } else { None @@ -380,14 +384,13 @@ fn parse_note_plaintext_without_memo_ivk( fn check_note_validity( note: &D::Note, - epk: &D::EphemeralPublicKey, + ephemeral_key: &EphemeralKeyBytes, cmstar_bytes: &D::ExtractedCommitmentBytes, ) -> NoteValidity { if &D::ExtractedCommitmentBytes::from(&D::cmstar(¬e)) == cmstar_bytes { - let epk_bytes = D::epk_bytes(epk); D::check_epk_bytes(¬e, |derived_esk| { if D::epk_bytes(&D::ka_derive_public(¬e, &derived_esk)) - .ct_eq(&epk_bytes) + .ct_eq(&ephemeral_key) .into() { NoteValidity::Valid @@ -416,9 +419,11 @@ pub fn try_compact_note_decryption>( output: &Output, ) -> Option<(D::Note, D::Recipient)> { assert_eq!(output.enc_ciphertext().len(), COMPACT_NOTE_SIZE); + let ephemeral_key = output.ephemeral_key(); - let shared_secret = D::ka_agree_dec(&ivk, output.epk()); - let key = D::kdf(shared_secret, &D::epk_bytes(output.epk())); + let epk = D::epk(&ephemeral_key)?; + let shared_secret = D::ka_agree_dec(&ivk, &epk); + let key = D::kdf(shared_secret, &ephemeral_key); // Start from block 1 to skip over Poly1305 keying output let mut plaintext = [0; COMPACT_NOTE_SIZE]; @@ -428,7 +433,7 @@ pub fn try_compact_note_decryption>( parse_note_plaintext_without_memo_ivk( domain, ivk, - output.epk(), + &ephemeral_key, &output.cmstar_bytes(), &plaintext, ) @@ -450,12 +455,7 @@ pub fn try_output_recovery_with_ovk>( cv: &D::ValueCommitment, out_ciphertext: &[u8], ) -> Option<(D::Note, D::Recipient, D::Memo)> { - let ock = D::derive_ock( - ovk, - &cv, - &output.cmstar_bytes(), - &D::epk_bytes(&output.epk()), - ); + let ock = D::derive_ock(ovk, &cv, &output.cmstar_bytes(), &output.ephemeral_key()); try_output_recovery_with_ock(domain, &ock, output, out_ciphertext) } @@ -488,11 +488,12 @@ pub fn try_output_recovery_with_ock>( let pk_d = D::extract_pk_d(&op)?; let esk = D::extract_esk(&op)?; + let ephemeral_key = output.ephemeral_key(); let shared_secret = D::ka_agree_enc(&esk, &pk_d); // The small-order point check at the point of output parsing rejects // non-canonical encodings, so reencoding here for the KDF should // be okay. - let key = D::kdf(shared_secret, &D::epk_bytes(output.epk())); + let key = D::kdf(shared_secret, &ephemeral_key); let mut plaintext = [0; ENC_CIPHERTEXT_SIZE]; assert_eq!( @@ -509,7 +510,7 @@ pub fn try_output_recovery_with_ock>( ); let (note, to) = - domain.parse_note_plaintext_without_memo_ovk(&pk_d, &esk, output.epk(), &plaintext)?; + domain.parse_note_plaintext_without_memo_ovk(&pk_d, &esk, &ephemeral_key, &plaintext)?; let memo = domain.extract_memo(&plaintext); // ZIP 212: Check that the esk provided to this function is consistent with the esk we @@ -521,7 +522,7 @@ pub fn try_output_recovery_with_ock>( } if let NoteValidity::Valid = - check_note_validity::(¬e, output.epk(), &output.cmstar_bytes()) + check_note_validity::(¬e, &ephemeral_key, &output.cmstar_bytes()) { Some((note, to, memo)) } else {