Use `EphemeralSecretKey, EphemeralPublicKey, SharedSecret` types in APIs

This commit is contained in:
Jack Grigg 2023-01-10 13:43:09 +00:00
parent bc99cd2634
commit 7f970bb82f
8 changed files with 78 additions and 82 deletions

View File

@ -394,13 +394,14 @@ mod tests {
GroupEncoding,
};
use rand_core::{OsRng, RngCore};
use zcash_note_encryption::Domain;
use zcash_primitives::{
consensus::{BlockHeight, Network},
constants::SPENDING_KEY_GENERATOR,
memo::MemoBytes,
merkle_tree::CommitmentTree,
sapling::{
note_encryption::{sapling_note_encryption, PreparedIncomingViewingKey},
note_encryption::{sapling_note_encryption, PreparedIncomingViewingKey, SaplingDomain},
util::generate_random_rseed,
value::NoteValue,
Note, Nullifier, SaplingIvk,
@ -474,7 +475,9 @@ mod tests {
&mut rng,
);
let cmu = note.cmu().to_bytes().to_vec();
let ephemeral_key = encryptor.epk().to_bytes().to_vec();
let ephemeral_key = SaplingDomain::<Network>::epk_bytes(encryptor.epk())
.0
.to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
// Create a fake CompactBlock containing the note

View File

@ -50,6 +50,7 @@ proptest = "1.0.0"
rand_core = "0.6"
regex = "1.4"
tempfile = "3"
zcash_note_encryption = { version = "0.2", path = "../components/zcash_note_encryption" }
zcash_proofs = { version = "0.9", path = "../zcash_proofs" }
zcash_primitives = { version = "0.9", path = "../zcash_primitives", features = ["test-dependencies"] }
zcash_address = { version = "0.2", path = "../components/zcash_address", features = ["test-dependencies"] }

View File

@ -1003,7 +1003,6 @@ extern crate assert_matches;
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use group::GroupEncoding;
use prost::Message;
use rand_core::{OsRng, RngCore};
use rusqlite::params;
@ -1012,14 +1011,17 @@ mod tests {
#[cfg(feature = "transparent-inputs")]
use zcash_primitives::{legacy, legacy::keys::IncomingViewingKey};
use zcash_note_encryption::Domain;
use zcash_primitives::{
block::BlockHash,
consensus::{BlockHeight, Network, NetworkUpgrade, Parameters},
legacy::TransparentAddress,
memo::MemoBytes,
sapling::{
note_encryption::sapling_note_encryption, util::generate_random_rseed,
value::NoteValue, Note, Nullifier, PaymentAddress,
note_encryption::{sapling_note_encryption, SaplingDomain},
util::generate_random_rseed,
value::NoteValue,
Note, Nullifier, PaymentAddress,
},
transaction::components::Amount,
zip32::{sapling::DiversifiableFullViewingKey, DiversifierIndex},
@ -1141,7 +1143,9 @@ mod tests {
&mut rng,
);
let cmu = note.cmu().to_bytes().to_vec();
let ephemeral_key = encryptor.epk().to_bytes().to_vec();
let ephemeral_key = SaplingDomain::<Network>::epk_bytes(encryptor.epk())
.0
.to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
// Create a fake CompactBlock containing the note
@ -1201,7 +1205,9 @@ mod tests {
&mut rng,
);
let cmu = note.cmu().to_bytes().to_vec();
let ephemeral_key = encryptor.epk().to_bytes().to_vec();
let ephemeral_key = SaplingDomain::<Network>::epk_bytes(encryptor.epk())
.0
.to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
CompactSaplingOutput {
@ -1228,7 +1234,9 @@ mod tests {
&mut rng,
);
let cmu = note.cmu().to_bytes().to_vec();
let ephemeral_key = encryptor.epk().to_bytes().to_vec();
let ephemeral_key = SaplingDomain::<Network>::epk_bytes(encryptor.epk())
.0
.to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
CompactSaplingOutput {

View File

@ -35,6 +35,14 @@ and this library adheres to Rust's notion of
- `PaymentAddress::from_parts`
- `PaymentAddress::pk_d`
- `note_encryption::SaplingDomain::DiversifiedTransmissionKey`
- `EphemeralSecretKey` is now used instead of `jubjub::Scalar` in the
following places:
- `Note::generate_or_derive_esk`
- `note_encryption::SaplingDomain::EphemeralSecretKey`
- `note_encryption::SaplingDomain::EphemeralPublicKey` is now
`EphemeralPublicKey` instead of `jubjub::ExtendedPoint`.
- `note_encryption::SaplingDomain::SharedSecret` is now `SharedSecret` instead
of `jubjub::SubgroupPoint`.
- Note commitments now use
`zcash_primitives::sapling::note::ExtractedNoteCommitment` instead of
`bls12_381::Scalar` in the following places:

View File

@ -383,14 +383,8 @@ impl EphemeralSecretKey {
pub struct EphemeralPublicKey(jubjub::ExtendedPoint);
impl EphemeralPublicKey {
/// TODO: Remove once public API is changed.
pub(crate) fn from_inner(epk: jubjub::ExtendedPoint) -> Self {
EphemeralPublicKey(epk)
}
/// TODO: Remove once public API is changed.
pub(crate) fn into_inner(self) -> jubjub::ExtendedPoint {
self.0
pub(crate) fn from_affine(epk: jubjub::AffinePoint) -> Self {
EphemeralPublicKey(epk.into())
}
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
@ -407,8 +401,8 @@ impl EphemeralPublicKey {
pub struct PreparedEphemeralPublicKey(PreparedBase);
impl PreparedEphemeralPublicKey {
pub(crate) fn new(epk: jubjub::ExtendedPoint) -> Self {
PreparedEphemeralPublicKey(PreparedBase::new(epk))
pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
PreparedEphemeralPublicKey(PreparedBase::new(epk.0))
}
pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
@ -425,16 +419,6 @@ impl PreparedEphemeralPublicKey {
pub struct SharedSecret(jubjub::SubgroupPoint);
impl SharedSecret {
/// TODO: Remove once public API is changed.
pub(crate) fn from_inner(epk: jubjub::SubgroupPoint) -> Self {
SharedSecret(epk)
}
/// TODO: Remove once public API is changed.
pub(crate) fn into_inner(self) -> jubjub::SubgroupPoint {
self.0
}
/// For checking test vectors only.
#[cfg(test)]
pub(crate) fn to_bytes(&self) -> [u8; 32] {

View File

@ -1,7 +1,9 @@
use group::{ff::Field, GroupEncoding};
use rand_core::{CryptoRng, RngCore};
use super::{value::NoteValue, Nullifier, NullifierDerivingKey, PaymentAddress};
use super::{
keys::EphemeralSecretKey, value::NoteValue, Nullifier, NullifierDerivingKey, PaymentAddress,
};
use crate::keys::prf_expand;
mod commitment;
@ -120,24 +122,30 @@ impl Note {
/// Derives `esk` from the internal `Rseed` value, or generates a random value if this
/// note was created with a v1 (i.e. pre-ZIP 212) note plaintext.
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> jubjub::Fr {
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
) -> EphemeralSecretKey {
self.generate_or_derive_esk_internal(rng)
}
pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(&self, rng: &mut R) -> jubjub::Fr {
pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(
&self,
rng: &mut R,
) -> EphemeralSecretKey {
match self.derive_esk() {
None => jubjub::Fr::random(rng),
None => EphemeralSecretKey(jubjub::Fr::random(rng)),
Some(esk) => esk,
}
}
/// Returns the derived `esk` if this note was created after ZIP 212 activated.
pub(crate) fn derive_esk(&self) -> Option<jubjub::Fr> {
pub(crate) fn derive_esk(&self) -> Option<EphemeralSecretKey> {
match self.rseed {
Rseed::BeforeZip212(_) => None,
Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide(
Rseed::AfterZip212(rseed) => Some(EphemeralSecretKey(jubjub::Fr::from_bytes_wide(
prf_expand(&rseed, &[0x05]).as_array(),
)),
))),
}
}
}

View File

@ -125,13 +125,13 @@ impl<P: consensus::Parameters> SaplingDomain<P> {
}
impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
type EphemeralSecretKey = jubjub::Scalar;
// It is acceptable for this to be a point because we enforce by consensus that
// points must not be small-order, and all points with non-canonical serialization
// are small-order.
type EphemeralPublicKey = jubjub::ExtendedPoint;
type EphemeralSecretKey = EphemeralSecretKey;
// It is acceptable for this to be a point rather than a byte array, because we
// enforce by consensus that points must not be small-order, and all points with
// non-canonical serialization are small-order.
type EphemeralPublicKey = EphemeralPublicKey;
type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey;
type SharedSecret = jubjub::SubgroupPoint;
type SharedSecret = SharedSecret;
type SymmetricKey = Blake2bHash;
type Note = Note;
type Recipient = PaymentAddress;
@ -159,30 +159,28 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
note: &Self::Note,
esk: &Self::EphemeralSecretKey,
) -> Self::EphemeralPublicKey {
EphemeralSecretKey(*esk)
.derive_public(note.recipient().g_d().into())
.into_inner()
esk.derive_public(note.recipient().g_d().into())
}
fn ka_agree_enc(
esk: &Self::EphemeralSecretKey,
pk_d: &Self::DiversifiedTransmissionKey,
) -> Self::SharedSecret {
EphemeralSecretKey(*esk).agree(pk_d).into_inner()
esk.agree(pk_d)
}
fn ka_agree_dec(
ivk: &Self::IncomingViewingKey,
epk: &Self::PreparedEphemeralPublicKey,
) -> Self::SharedSecret {
epk.agree(ivk).into_inner()
epk.agree(ivk)
}
/// Sapling KDF for note encryption.
///
/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
fn kdf(dhsecret: jubjub::SubgroupPoint, epk: &EphemeralKeyBytes) -> Blake2bHash {
SharedSecret::from_inner(dhsecret).kdf_sapling(epk)
fn kdf(dhsecret: SharedSecret, epk: &EphemeralKeyBytes) -> Blake2bHash {
dhsecret.kdf_sapling(epk)
}
fn note_plaintext_bytes(
@ -231,22 +229,20 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
) -> OutPlaintextBytes {
let mut input = [0u8; OUT_PLAINTEXT_SIZE];
input[0..32].copy_from_slice(&note.recipient().pk_d().to_bytes());
input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(esk.to_repr().as_ref());
input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(esk.0.to_repr().as_ref());
OutPlaintextBytes(input)
}
fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes {
EphemeralPublicKey::from_inner(*epk).to_bytes()
epk.to_bytes()
}
fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option<Self::EphemeralPublicKey> {
// ZIP 216: We unconditionally reject non-canonical encodings, because these have
// always been rejected by consensus (due to small-order checks).
// https://zips.z.cash/zip-0216#specification
let res: Option<EphemeralPublicKey> =
EphemeralPublicKey::from_bytes(&ephemeral_key.0).into();
res.map(|epk| epk.into_inner())
EphemeralPublicKey::from_bytes(&ephemeral_key.0).into()
}
fn parse_note_plaintext_without_memo_ivk(
@ -267,12 +263,7 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
plaintext: &NotePlaintextBytes,
) -> Option<(Self::Note, Self::Recipient)> {
sapling_parse_note_plaintext_without_memo(self, &plaintext.0, |diversifier| {
if EphemeralSecretKey(*esk)
.derive_public(diversifier.g_d()?.into())
.to_bytes()
.0
== ephemeral_key.0
{
if esk.derive_public(diversifier.g_d()?.into()).to_bytes().0 == ephemeral_key.0 {
Some(*pk_d)
} else {
None
@ -292,13 +283,12 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
}
fn extract_esk(op: &OutPlaintextBytes) -> Option<Self::EphemeralSecretKey> {
let res: Option<EphemeralSecretKey> = EphemeralSecretKey::from_bytes(
EphemeralSecretKey::from_bytes(
op.0[32..OUT_PLAINTEXT_SIZE]
.try_into()
.expect("slice is the correct length"),
)
.into();
res.map(|esk| esk.0)
.into()
}
fn extract_memo(&self, plaintext: &NotePlaintextBytes) -> Self::Memo {
@ -310,11 +300,7 @@ impl<P: consensus::Parameters> BatchDomain for SaplingDomain<P> {
fn batch_kdf<'a>(
items: impl Iterator<Item = (Option<Self::SharedSecret>, &'a EphemeralKeyBytes)>,
) -> Vec<Option<Self::SymmetricKey>> {
let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items
.map(|(shared_secret, ephemeral_key)| {
(shared_secret.map(SharedSecret::from_inner), ephemeral_key)
})
.unzip();
let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip();
SharedSecret::batch_to_affine(shared_secrets)
.zip(ephemeral_keys.into_iter())
@ -333,9 +319,9 @@ impl<P: consensus::Parameters> BatchDomain for SaplingDomain<P> {
.zip(ephemeral_keys.into_iter())
.map(|(epk, ephemeral_key)| {
(
epk.map(jubjub::ExtendedPoint::from)
.map(Self::prepare_epk)
.into(),
Option::from(epk)
.map(EphemeralPublicKey::from_affine)
.map(Self::prepare_epk),
ephemeral_key,
)
})
@ -532,7 +518,7 @@ mod tests {
keys::OutgoingViewingKey,
memo::MemoBytes,
sapling::{
keys::{DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey},
keys::{DiversifiedTransmissionKey, EphemeralSecretKey},
note::ExtractedNoteCommitment,
note_encryption::PreparedIncomingViewingKey,
util::generate_random_rseed,
@ -611,14 +597,14 @@ mod tests {
MemoBytes::empty(),
&mut rng,
);
let epk = EphemeralPublicKey::from_inner(*ne.epk());
let epk = ne.epk();
let ock = prf_ock(&ovk, &cv, &cmu.to_bytes(), &epk.to_bytes());
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu, &mut rng);
let output = OutputDescription::from_parts(
cv,
cmu,
epk.to_bytes().into(),
epk.to_bytes(),
ne.encrypt_note_plaintext(),
out_ciphertext,
[0u8; GROTH_PROOF_SIZE],
@ -1415,14 +1401,14 @@ mod tests {
let rcm = read_jubjub_scalar!(tv.rcm);
let cv = read_cv!(tv.cv);
let cmu = read_cmu!(tv.cmu);
let esk = read_jubjub_scalar!(tv.esk);
let esk = EphemeralSecretKey(read_jubjub_scalar!(tv.esk));
let ephemeral_key = EphemeralKeyBytes(tv.epk);
//
// Test the individual components
//
let shared_secret = EphemeralSecretKey(esk).agree(&pk_d);
let shared_secret = esk.agree(&pk_d);
assert_eq!(shared_secret.to_bytes(), tv.shared_secret);
let k_enc = shared_secret.kdf_sapling(&ephemeral_key);

View File

@ -4,7 +4,6 @@ use core::fmt;
use std::sync::mpsc::Sender;
use ff::Field;
use group::GroupEncoding;
use rand::{seq::SliceRandom, RngCore};
use crate::{
@ -13,7 +12,7 @@ use crate::{
memo::MemoBytes,
merkle_tree::MerklePath,
sapling::{
keys::{EphemeralSecretKey, SaplingIvk},
keys::SaplingIvk,
note_encryption::sapling_note_encryption,
prover::TxProver,
redjubjub::{PrivateKey, Signature},
@ -127,7 +126,7 @@ impl SaplingOutputInfo {
let (zkproof, cv) = prover.output_proof(
ctx,
*encryptor.esk(),
encryptor.esk().0,
self.to,
self.note.rcm(),
self.note.value().inner(),
@ -138,12 +137,12 @@ impl SaplingOutputInfo {
let enc_ciphertext = encryptor.encrypt_note_plaintext();
let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu, rng);
let epk = *encryptor.epk();
let epk = encryptor.epk();
OutputDescription {
cv,
cmu,
ephemeral_key: epk.to_bytes().into(),
ephemeral_key: epk.to_bytes(),
enc_ciphertext,
out_ciphertext,
zkproof,
@ -471,8 +470,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
Note::from_parts(payment_address, NoteValue::from_raw(0), rseed)
};
let esk =
EphemeralSecretKey(dummy_note.generate_or_derive_esk_internal(&mut rng));
let esk = dummy_note.generate_or_derive_esk_internal(&mut rng);
let epk = esk.derive_public(
dummy_note
.recipient()