diff --git a/CHANGELOG.md b/CHANGELOG.md index cedc180..9af97c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this library adheres to Rust's notion of ## [Unreleased] +## [0.4.1] - 2024-12-06 +### Added +- `zcash_note_encryption::try_output_recovery_with_pkd_esk` + ## [0.4.0] - 2023-06-06 ### Changed - The `esk` and `ephemeral_key` arguments have been removed from diff --git a/Cargo.lock b/Cargo.lock index c04cb40..0d41754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,7 +150,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "zcash_note_encryption" -version = "0.4.0" +version = "0.4.1" dependencies = [ "chacha20", "chacha20poly1305", diff --git a/Cargo.toml b/Cargo.toml index 34d359e..fccff47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_note_encryption" description = "Note encryption for Zcash transactions" -version = "0.4.0" +version = "0.4.1" authors = [ "Jack Grigg <jack@electriccoin.co>", "Kris Nuttycombe <kris@electriccoin.co>" diff --git a/src/batch.rs b/src/batch.rs index ad70416..59577b5 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -75,12 +75,11 @@ where key_chunk .iter() .zip(ivks.iter().enumerate()) - .filter_map(|(key, (i, ivk))| { + .find_map(|(key, (i, ivk))| { key.as_ref() .and_then(|key| decrypt_inner(domain, ivk, ephemeral_key, output, key)) .map(|out| (out, i)) }) - .next() }) .collect::<Vec<Option<_>>>() } diff --git a/src/lib.rs b/src/lib.rs index 16c089b..d5b1274 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -636,8 +636,6 @@ pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D, ENC_CIP output: &Output, out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE], ) -> Option<(D::Note, D::Recipient, D::Memo)> { - let enc_ciphertext = output.enc_ciphertext(); - let mut op = OutPlaintextBytes([0; OUT_PLAINTEXT_SIZE]); op.0.copy_from_slice(&out_ciphertext[..OUT_PLAINTEXT_SIZE]); @@ -653,6 +651,27 @@ pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D, ENC_CIP let pk_d = D::extract_pk_d(&op)?; let esk = D::extract_esk(&op)?; + try_output_recovery_with_pkd_esk(domain, pk_d, esk, output) +} + +/// Recovery of the full note plaintext by the sender. +/// +/// Attempts to decrypt and validate the given shielded output using the given `pk_d` and `esk`. If +/// successful, the corresponding note and memo are returned, along with the address to which the +/// note was sent. +/// +/// Implements part of section 4.19.3 of the +/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptovk). +/// For decryption using a Full Viewing Key see [`try_output_recovery_with_ovk`]. +pub fn try_output_recovery_with_pkd_esk< + D: Domain, + Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>, +>( + domain: &D, + pk_d: D::DiversifiedTransmissionKey, + esk: D::EphemeralSecretKey, + output: &Output, +) -> Option<(D::Note, D::Recipient, D::Memo)> { 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 @@ -660,6 +679,7 @@ pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D, ENC_CIP // be okay. let key = D::kdf(shared_secret, &ephemeral_key); + let enc_ciphertext = output.enc_ciphertext(); let mut plaintext = NotePlaintextBytes([0; NOTE_PLAINTEXT_SIZE]); plaintext .0