Use `EphemeralSecretKey, EphemeralPublicKey, SharedSecret` types in APIs
This commit is contained in:
parent
bc99cd2634
commit
7f970bb82f
|
@ -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
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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(),
|
||||
)),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(¬e.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);
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue