diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 3b6e83b3a..e728c5e66 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -695,10 +695,47 @@ mod tests { [u8; ENC_CIPHERTEXT_SIZE], [u8; OUT_CIPHERTEXT_SIZE], ) { - let diversifier = Diversifier([0; 11]); let ivk = Fs::random(&mut rng); + + let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(ivk, rng); + + assert!(try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext).is_some()); + assert!(try_sapling_compact_note_decryption( + &ivk, + &epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ) + .is_some()); + assert!(try_sapling_output_recovery( + &ovk, + &cv, + &cmu, + &epk, + &enc_ciphertext, + &out_ciphertext + ) + .is_some()); + + (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) + } + + fn random_enc_ciphertext_with( + ivk: Fs, + mut rng: &mut R, + ) -> ( + OutgoingViewingKey, + Fs, + edwards::Point, + Fr, + edwards::Point, + [u8; ENC_CIPHERTEXT_SIZE], + [u8; OUT_CIPHERTEXT_SIZE], + ) { + let diversifier = Diversifier([0; 11]); let pk_d = diversifier.g_d::(&JUBJUB).unwrap().mul(ivk, &JUBJUB); - let pa = PaymentAddress::from_parts(diversifier, pk_d).unwrap(); + let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d); // Construct the value commitment for the proof instance let value = 100; @@ -719,24 +756,6 @@ mod tests { let enc_ciphertext = ne.encrypt_note_plaintext(); let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); - assert!(try_sapling_note_decryption(&ivk, epk, &cmu, &enc_ciphertext).is_some()); - assert!(try_sapling_compact_note_decryption( - &ivk, - epk, - &cmu, - &enc_ciphertext[..COMPACT_NOTE_SIZE] - ) - .is_some()); - assert!(try_sapling_output_recovery( - &ovk, - &cv, - &cmu, - &epk, - &enc_ciphertext, - &out_ciphertext - ) - .is_some()); - ( ovk, ivk, @@ -1253,6 +1272,20 @@ mod tests { ); } + #[test] + fn recovery_with_invalid_pk_d() { + let mut rng = OsRng; + + let ivk = Fs::zero(); + let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) = + random_enc_ciphertext_with(ivk, &mut rng); + + assert_eq!( + try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext), + None + ); + } + #[test] fn test_vectors() { let test_vectors = crate::test_vectors::note_encryption::make_test_vectors(); diff --git a/zcash_primitives/src/primitives.rs b/zcash_primitives/src/primitives.rs index 4f60c645e..a336673a7 100644 --- a/zcash_primitives/src/primitives.rs +++ b/zcash_primitives/src/primitives.rs @@ -151,6 +151,17 @@ impl PaymentAddress { } } + /// Constructs a PaymentAddress from a diversifier and a Jubjub point. + /// + /// Only for test code, as this explicitly bypasses the invariant. + #[cfg(test)] + pub(crate) fn from_parts_unchecked( + diversifier: Diversifier, + pk_d: edwards::Point, + ) -> Self { + PaymentAddress { pk_d, diversifier } + } + /// Parses a PaymentAddress from bytes. pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option { let diversifier = {