Merge pull request #290 from str4d/273-remove-decryption-epk-subgroup-check
Take epk as a jubjub::ExtendedPoint for note decryption
This commit is contained in:
commit
e0587e752b
|
@ -1,4 +1,3 @@
|
|||
use group::cofactor::CofactorGroup;
|
||||
use zcash_primitives::{
|
||||
consensus::{self, BlockHeight},
|
||||
note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo},
|
||||
|
@ -45,18 +44,12 @@ pub fn decrypt_transaction<P: consensus::Parameters>(
|
|||
.collect();
|
||||
|
||||
for (index, output) in tx.shielded_outputs.iter().enumerate() {
|
||||
let epk = output.ephemeral_key.into_subgroup();
|
||||
if epk.is_none().into() {
|
||||
continue;
|
||||
}
|
||||
let epk = epk.unwrap();
|
||||
|
||||
for (account, (ivk, ovk)) in vks.iter().enumerate() {
|
||||
let ((note, to, memo), outgoing) = match try_sapling_note_decryption(
|
||||
params,
|
||||
height,
|
||||
ivk,
|
||||
&epk,
|
||||
&output.ephemeral_key,
|
||||
&output.cmu,
|
||||
&output.enc_ciphertext,
|
||||
) {
|
||||
|
@ -67,7 +60,7 @@ pub fn decrypt_transaction<P: consensus::Parameters>(
|
|||
ovk,
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&epk,
|
||||
&output.ephemeral_key,
|
||||
&output.enc_ciphertext,
|
||||
&output.out_ciphertext,
|
||||
) {
|
||||
|
|
|
@ -86,8 +86,8 @@ impl compact_formats::CompactOutput {
|
|||
/// A convenience method that parses [`CompactOutput.epk`].
|
||||
///
|
||||
/// [`CompactOutput.epk`]: #structfield.epk
|
||||
pub fn epk(&self) -> Result<jubjub::SubgroupPoint, ()> {
|
||||
let p = jubjub::SubgroupPoint::from_bytes(&self.epk[..].try_into().map_err(|_| ())?);
|
||||
pub fn epk(&self) -> Result<jubjub::ExtendedPoint, ()> {
|
||||
let p = jubjub::ExtendedPoint::from_bytes(&self.epk[..].try_into().map_err(|_| ())?);
|
||||
if p.is_some().into() {
|
||||
Ok(p.unwrap())
|
||||
} else {
|
||||
|
|
|
@ -35,7 +35,7 @@ pub struct WalletShieldedSpend {
|
|||
pub struct WalletShieldedOutput {
|
||||
pub index: usize,
|
||||
pub cmu: bls12_381::Scalar,
|
||||
pub epk: jubjub::SubgroupPoint,
|
||||
pub epk: jubjub::ExtendedPoint,
|
||||
pub account: usize,
|
||||
pub note: Note,
|
||||
pub to: PaymentAddress,
|
||||
|
|
|
@ -370,7 +370,6 @@ pub fn create_to_address<DB: AsRef<Path>, P: consensus::Parameters>(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::cofactor::CofactorGroup;
|
||||
use rusqlite::Connection;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
|
@ -832,7 +831,7 @@ mod tests {
|
|||
&extfvk.fvk.ovk,
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&output.ephemeral_key.into_subgroup().unwrap(),
|
||||
&output.ephemeral_key,
|
||||
&output.enc_ciphertext,
|
||||
&output.out_ciphertext,
|
||||
)
|
||||
|
|
|
@ -45,6 +45,10 @@ rand_xorshift = "0.2"
|
|||
[features]
|
||||
transparent-inputs = ["ripemd160", "secp256k1"]
|
||||
|
||||
[[bench]]
|
||||
name = "note_decryption"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "pedersen_hash"
|
||||
harness = false
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use ff::Field;
|
||||
use rand_core::OsRng;
|
||||
use zcash_primitives::{
|
||||
consensus::{NetworkUpgrade::Canopy, Parameters, TestNetwork},
|
||||
note_encryption::{try_sapling_note_decryption, Memo, SaplingNoteEncryption},
|
||||
primitives::{Diversifier, PaymentAddress, ValueCommitment},
|
||||
transaction::components::{OutputDescription, GROTH_PROOF_SIZE},
|
||||
util::generate_random_rseed,
|
||||
};
|
||||
|
||||
fn bench_note_decryption(c: &mut Criterion) {
|
||||
let mut rng = OsRng;
|
||||
let height = TestNetwork::activation_height(Canopy).unwrap();
|
||||
|
||||
let valid_ivk = jubjub::Fr::random(&mut rng);
|
||||
let invalid_ivk = jubjub::Fr::random(&mut rng);
|
||||
|
||||
// Construct a fake Sapling output as if we had just deserialized a transaction.
|
||||
let output = {
|
||||
let diversifier = Diversifier([0; 11]);
|
||||
let pk_d = diversifier.g_d().unwrap() * valid_ivk;
|
||||
let pa = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
|
||||
|
||||
let rseed = generate_random_rseed::<TestNetwork, _>(height, &mut rng);
|
||||
|
||||
// Construct the value commitment for the proof instance
|
||||
let value = 100;
|
||||
let value_commitment = ValueCommitment {
|
||||
value,
|
||||
randomness: jubjub::Fr::random(&mut rng),
|
||||
};
|
||||
let cv = value_commitment.commitment().into();
|
||||
|
||||
let note = pa.create_note(value, rseed).unwrap();
|
||||
let cmu = note.cmu();
|
||||
|
||||
let mut ne = SaplingNoteEncryption::new(None, note, pa, Memo::default(), &mut rng);
|
||||
let ephemeral_key = ne.epk().clone().into();
|
||||
let enc_ciphertext = ne.encrypt_note_plaintext();
|
||||
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);
|
||||
|
||||
OutputDescription {
|
||||
cv,
|
||||
cmu,
|
||||
ephemeral_key,
|
||||
enc_ciphertext,
|
||||
out_ciphertext,
|
||||
zkproof: [0; GROTH_PROOF_SIZE],
|
||||
}
|
||||
};
|
||||
|
||||
let mut group = c.benchmark_group("Sapling note decryption");
|
||||
|
||||
group.bench_function("valid", |b| {
|
||||
b.iter(|| {
|
||||
try_sapling_note_decryption::<TestNetwork>(
|
||||
height,
|
||||
&valid_ivk,
|
||||
&output.ephemeral_key,
|
||||
&output.cmu,
|
||||
&output.enc_ciphertext,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
group.bench_function("invalid", |b| {
|
||||
b.iter(|| {
|
||||
try_sapling_note_decryption::<TestNetwork>(
|
||||
height,
|
||||
&invalid_ivk,
|
||||
&output.ephemeral_key,
|
||||
&output.cmu,
|
||||
&output.enc_ciphertext,
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_note_decryption);
|
||||
criterion_main!(benches);
|
|
@ -142,7 +142,7 @@ pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubju
|
|||
/// Sapling KDF for note encryption.
|
||||
///
|
||||
/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
|
||||
fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::SubgroupPoint) -> Blake2bHash {
|
||||
fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::ExtendedPoint) -> Blake2bHash {
|
||||
Blake2bParams::new()
|
||||
.hash_length(32)
|
||||
.personal(KDF_SAPLING_PERSONALIZATION)
|
||||
|
@ -174,7 +174,7 @@ pub fn prf_ock(
|
|||
ovk: &OutgoingViewingKey,
|
||||
cv: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
) -> OutgoingCipherKey {
|
||||
OutgoingCipherKey(
|
||||
Blake2bParams::new()
|
||||
|
@ -287,7 +287,7 @@ impl<R: RngCore + CryptoRng> SaplingNoteEncryption<R> {
|
|||
/// Generates `encCiphertext` for this note.
|
||||
pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] {
|
||||
let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d().into());
|
||||
let key = kdf_sapling(shared_secret, &self.epk);
|
||||
let key = kdf_sapling(shared_secret, &self.epk.into());
|
||||
|
||||
// Note plaintext encoding is defined in section 5.5 of the Zcash Protocol
|
||||
// Specification.
|
||||
|
@ -328,7 +328,7 @@ impl<R: RngCore + CryptoRng> SaplingNoteEncryption<R> {
|
|||
cmu: &bls12_381::Scalar,
|
||||
) -> [u8; OUT_CIPHERTEXT_SIZE] {
|
||||
let (ock, input) = if let Some(ovk) = &self.ovk {
|
||||
let ock = prf_ock(ovk, &cv, &cmu, &self.epk);
|
||||
let ock = prf_ock(ovk, &cv, &cmu, &self.epk.into());
|
||||
|
||||
let mut input = [0u8; OUT_PLAINTEXT_SIZE];
|
||||
input[0..32].copy_from_slice(&self.note.pk_d.to_bytes());
|
||||
|
@ -362,7 +362,7 @@ fn parse_note_plaintext_without_memo<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ivk: &jubjub::Fr,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
plaintext: &[u8],
|
||||
) -> Option<(Note, PaymentAddress)> {
|
||||
|
@ -399,7 +399,8 @@ fn parse_note_plaintext_without_memo<P: consensus::Parameters>(
|
|||
}
|
||||
|
||||
if let Some(derived_esk) = note.derive_esk() {
|
||||
if (note.g_d * derived_esk) != *epk {
|
||||
// This enforces that epk is a jubjub::SubgroupPoint.
|
||||
if (note.g_d * derived_esk).to_bytes() != epk.to_bytes() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -442,13 +443,13 @@ pub fn try_sapling_note_decryption<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ivk: &jubjub::Fr,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
enc_ciphertext: &[u8],
|
||||
) -> Option<(Note, PaymentAddress, Memo)> {
|
||||
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
|
||||
|
||||
let shared_secret = sapling_ka_agree(ivk, epk.into());
|
||||
let shared_secret = sapling_ka_agree(ivk, &epk);
|
||||
let key = kdf_sapling(shared_secret, &epk);
|
||||
|
||||
let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
|
||||
|
@ -486,13 +487,13 @@ pub fn try_sapling_compact_note_decryption<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ivk: &jubjub::Fr,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
enc_ciphertext: &[u8],
|
||||
) -> Option<(Note, PaymentAddress)> {
|
||||
assert_eq!(enc_ciphertext.len(), COMPACT_NOTE_SIZE);
|
||||
|
||||
let shared_secret = sapling_ka_agree(ivk, epk.into());
|
||||
let shared_secret = sapling_ka_agree(ivk, epk);
|
||||
let key = kdf_sapling(shared_secret, &epk);
|
||||
|
||||
// Start from block 1 to skip over Poly1305 keying output
|
||||
|
@ -516,7 +517,7 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
|
|||
height: BlockHeight,
|
||||
ock: &OutgoingCipherKey,
|
||||
cmu: &bls12_381::Scalar,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
enc_ciphertext: &[u8],
|
||||
out_ciphertext: &[u8],
|
||||
) -> Option<(Note, PaymentAddress, Memo)> {
|
||||
|
@ -589,7 +590,7 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
|
|||
memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]);
|
||||
|
||||
let diversifier = Diversifier(d);
|
||||
if diversifier.g_d()? * esk != *epk {
|
||||
if (diversifier.g_d()? * esk).to_bytes() != epk.to_bytes() {
|
||||
// Published epk doesn't match calculated epk
|
||||
return None;
|
||||
}
|
||||
|
@ -624,7 +625,7 @@ pub fn try_sapling_output_recovery<P: consensus::Parameters>(
|
|||
ovk: &OutgoingViewingKey,
|
||||
cv: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
enc_ciphertext: &[u8],
|
||||
out_ciphertext: &[u8],
|
||||
) -> Option<(Note, PaymentAddress, Memo)> {
|
||||
|
@ -794,7 +795,7 @@ mod tests {
|
|||
jubjub::Fr,
|
||||
jubjub::ExtendedPoint,
|
||||
bls12_381::Scalar,
|
||||
jubjub::SubgroupPoint,
|
||||
jubjub::ExtendedPoint,
|
||||
[u8; ENC_CIPHERTEXT_SIZE],
|
||||
[u8; OUT_CIPHERTEXT_SIZE],
|
||||
) {
|
||||
|
@ -859,7 +860,7 @@ mod tests {
|
|||
jubjub::Fr,
|
||||
jubjub::ExtendedPoint,
|
||||
bls12_381::Scalar,
|
||||
jubjub::SubgroupPoint,
|
||||
jubjub::ExtendedPoint,
|
||||
[u8; ENC_CIPHERTEXT_SIZE],
|
||||
[u8; OUT_CIPHERTEXT_SIZE],
|
||||
) {
|
||||
|
@ -882,7 +883,7 @@ mod tests {
|
|||
|
||||
let ovk = OutgoingViewingKey([0; 32]);
|
||||
let mut ne = SaplingNoteEncryption::new(Some(ovk), note, pa, Memo([0; 512]), &mut rng);
|
||||
let epk = ne.epk().clone();
|
||||
let epk = ne.epk().clone().into();
|
||||
let enc_ciphertext = ne.encrypt_note_plaintext();
|
||||
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);
|
||||
let ock = prf_ock(&ovk, &cv, &cmu, &epk);
|
||||
|
@ -894,7 +895,7 @@ mod tests {
|
|||
ovk: &OutgoingViewingKey,
|
||||
cv: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
epk: &jubjub::SubgroupPoint,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
enc_ciphertext: &mut [u8; ENC_CIPHERTEXT_SIZE],
|
||||
out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE],
|
||||
modify_plaintext: impl Fn(&mut [u8; NOTE_PLAINTEXT_SIZE]),
|
||||
|
@ -1014,7 +1015,7 @@ mod tests {
|
|||
&TEST_NETWORK,
|
||||
height,
|
||||
&ivk,
|
||||
&jubjub::SubgroupPoint::random(&mut rng),
|
||||
&jubjub::ExtendedPoint::random(&mut rng),
|
||||
&cmu,
|
||||
&enc_ciphertext
|
||||
),
|
||||
|
@ -1225,7 +1226,7 @@ mod tests {
|
|||
&TEST_NETWORK,
|
||||
height,
|
||||
&ivk,
|
||||
&jubjub::SubgroupPoint::random(&mut rng),
|
||||
&jubjub::ExtendedPoint::random(&mut rng),
|
||||
&cmu,
|
||||
&enc_ciphertext[..COMPACT_NOTE_SIZE]
|
||||
),
|
||||
|
@ -1511,7 +1512,7 @@ mod tests {
|
|||
&ovk,
|
||||
&cv,
|
||||
&cmu,
|
||||
&jubjub::SubgroupPoint::random(&mut rng),
|
||||
&jubjub::ExtendedPoint::random(&mut rng),
|
||||
&enc_ciphertext,
|
||||
&out_ciphertext
|
||||
),
|
||||
|
@ -1524,7 +1525,7 @@ mod tests {
|
|||
height,
|
||||
&ock,
|
||||
&cmu,
|
||||
&jubjub::SubgroupPoint::random(&mut rng),
|
||||
&jubjub::ExtendedPoint::random(&mut rng),
|
||||
&enc_ciphertext,
|
||||
&out_ciphertext
|
||||
),
|
||||
|
@ -1841,7 +1842,7 @@ mod tests {
|
|||
let cv = read_point!(tv.cv);
|
||||
let cmu = read_bls12_381_scalar!(tv.cmu);
|
||||
let esk = read_jubjub_scalar!(tv.esk);
|
||||
let epk = read_point!(tv.epk).into_subgroup().unwrap();
|
||||
let epk = read_point!(tv.epk);
|
||||
|
||||
//
|
||||
// Test the individual components
|
||||
|
@ -1915,7 +1916,7 @@ mod tests {
|
|||
let mut ne = SaplingNoteEncryption::new(Some(ovk), note, to, Memo(tv.memo), OsRng);
|
||||
// Swap in the ephemeral keypair from the test vectors
|
||||
ne.esk = esk;
|
||||
ne.epk = epk;
|
||||
ne.epk = epk.into_subgroup().unwrap();
|
||||
|
||||
assert_eq!(&ne.encrypt_note_plaintext()[..], &tv.c_enc[..]);
|
||||
assert_eq!(&ne.encrypt_outgoing_plaintext(&cv, &cmu)[..], &tv.c_out[..]);
|
||||
|
|
Loading…
Reference in New Issue