diff --git a/ironfish-rust-wasm/src/keys/ephemeral.rs b/ironfish-rust-wasm/src/keys/ephemeral.rs new file mode 100644 index 0000000000..6aa0b96f85 --- /dev/null +++ b/ironfish-rust-wasm/src/keys/ephemeral.rs @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::{ + errors::IronfishError, + primitives::{Fr, SubgroupPoint}, + wasm_bindgen_wrapper, +}; +use wasm_bindgen::prelude::*; + +wasm_bindgen_wrapper! { + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct EphemeralKeyPair(ironfish::keys::EphemeralKeyPair); +} + +#[wasm_bindgen] +impl EphemeralKeyPair { + #[wasm_bindgen(constructor)] + pub fn deserialize(bytes: &[u8]) -> Result { + Ok(Self(ironfish::keys::EphemeralKeyPair::read(bytes)?)) + } + + #[wasm_bindgen] + pub fn serialize(&self) -> Vec { + let mut buf = Vec::new(); + self.0 + .write(&mut buf) + .expect("failed to serialize ephemeral key pair"); + buf + } + + #[wasm_bindgen] + pub fn random() -> Self { + Self(ironfish::keys::EphemeralKeyPair::new()) + } + + #[wasm_bindgen(getter)] + pub fn secret(&self) -> Fr { + self.0.secret().to_owned().into() + } + + #[wasm_bindgen(getter)] + pub fn public(&self) -> SubgroupPoint { + self.0.public().to_owned().into() + } +} diff --git a/ironfish-rust-wasm/src/keys/mod.rs b/ironfish-rust-wasm/src/keys/mod.rs index b6ed99d489..51c86e8760 100644 --- a/ironfish-rust-wasm/src/keys/mod.rs +++ b/ironfish-rust-wasm/src/keys/mod.rs @@ -2,12 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +mod ephemeral; mod mnemonics; mod proof_generation_key; mod public_address; mod sapling_key; mod view_keys; +pub use ephemeral::EphemeralKeyPair; pub use mnemonics::Language; pub use proof_generation_key::ProofGenerationKey; pub use public_address::PublicAddress; diff --git a/ironfish-rust/src/keys/ephemeral.rs b/ironfish-rust/src/keys/ephemeral.rs index dd106d0d70..2368a8f9c2 100644 --- a/ironfish-rust/src/keys/ephemeral.rs +++ b/ironfish-rust/src/keys/ephemeral.rs @@ -1,15 +1,18 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::errors::{IronfishError, IronfishErrorKind}; use ff::Field; use ironfish_zkp::constants::PUBLIC_KEY_GENERATOR; use rand::thread_rng; +use std::io; /// Diffie Hellman key exchange pair as used in note encryption. /// /// This can be used according to the protocol described in /// [`crate::keys::shared_secret`] -#[derive(Default)] +#[derive(Default, Clone, PartialEq, Eq, Debug)] pub struct EphemeralKeyPair { secret: ironfish_jubjub::Fr, public: ironfish_jubjub::SubgroupPoint, @@ -17,12 +20,22 @@ pub struct EphemeralKeyPair { impl EphemeralKeyPair { pub fn new() -> Self { - let secret = ironfish_jubjub::Fr::random(thread_rng()); + loop { + let secret = ironfish_jubjub::Fr::random(thread_rng()); + if let Ok(key_pair) = Self::from_secret(secret) { + break key_pair; + } + } + } - Self { + pub fn from_secret(secret: ironfish_jubjub::Fr) -> Result { + if secret == ironfish_jubjub::Fr::zero() || secret == ironfish_jubjub::Fr::one() { + return Err(IronfishError::new(IronfishErrorKind::InvalidSecret)); + } + Ok(Self { secret, public: *PUBLIC_KEY_GENERATOR * secret, - } + }) } pub fn secret(&self) -> &ironfish_jubjub::Fr { @@ -32,6 +45,20 @@ impl EphemeralKeyPair { pub fn public(&self) -> &ironfish_jubjub::SubgroupPoint { &self.public } + + pub fn read(mut reader: R) -> Result { + let mut secret_bytes = [0u8; 32]; + reader.read_exact(&mut secret_bytes)?; + let secret = Option::from(ironfish_jubjub::Fr::from_bytes(&secret_bytes)) + .ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidData))?; + Self::from_secret(secret) + } + + pub fn write(&self, mut writer: W) -> Result<(), IronfishError> { + let secret_bytes = self.secret.to_bytes(); + writer.write_all(&secret_bytes)?; + Ok(()) + } } #[cfg(test)]