diff --git a/src/error.rs b/src/error.rs index 558b3c8..e495539 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,6 @@ pub struct Error { } pub type Result = std::result::Result; -pub type StdResult = std::result::Result; /// MWixnet error types #[derive(Clone, Debug, Eq, Fail, PartialEq)] @@ -18,9 +17,6 @@ pub enum ErrorKind { /// Error from secp256k1-zkp library #[fail(display = "Secp Error")] SecpError, - /// Invalid key length for MAC initialization - #[fail(display = "InvalidKeyLength")] - InvalidKeyLength, /// Wraps an io error produced when reading or writing #[fail(display = "IO Error: {}", _0)] IOErr(String, io::ErrorKind), @@ -78,10 +74,6 @@ impl Error { inner: Context::new(kind), } } - - pub fn message(&self) -> String { - format!("{}", self).into() - } } impl From for Error { @@ -106,14 +98,6 @@ impl From for Error { } } -impl From for Error { - fn from(_error: hmac::digest::InvalidLength) -> Error { - Error { - inner: Context::new(ErrorKind::InvalidKeyLength), - } - } -} - impl From for Error { fn from(e: grin_api::Error) -> Error { Error { diff --git a/src/onion.rs b/src/onion.rs index cc59280..935778f 100644 --- a/src/onion.rs +++ b/src/onion.rs @@ -1,16 +1,19 @@ -use crate::error::Result; use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret}; use crate::types::Payload; +use crate::onion::OnionError::{InvalidKeyLength, SerializationError}; use chacha20::cipher::{NewCipher, StreamCipher}; use chacha20::{ChaCha20, Key, Nonce}; use grin_core::ser::{self, ProtocolVersion, Writeable, Writer}; use grin_util::{self, ToHex}; +use hmac::digest::InvalidLength; use hmac::{Hmac, Mac}; use serde::ser::SerializeStruct; use serde::Deserialize; use sha2::{Digest, Sha256}; use std::fmt; +use std::result::Result; +use thiserror::Error; type HmacSha256 = Hmac; type RawBytes = Vec; @@ -27,14 +30,14 @@ pub struct Onion { } impl Onion { - pub fn serialize(&self) -> Result> { + pub fn serialize(&self) -> Result, ser::Error> { let mut vec = vec![]; ser::serialize_default(&mut vec, &self)?; Ok(vec) } /// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload - pub fn peel_layer(&self, secret_key: &SecretKey) -> Result<(Payload, Onion)> { + pub fn peel_layer(&self, secret_key: &SecretKey) -> Result<(Payload, Onion), OnionError> { let secp = Secp256k1::new(); let shared_secret = SharedSecret::new(&secp, &self.ephemeral_pubkey, &secret_key); @@ -42,7 +45,8 @@ impl Onion { let mut decrypted_bytes = self.enc_payloads[0].clone(); cipher.apply_keystream(&mut decrypted_bytes); - let decrypted_payload = Payload::deserialize(&decrypted_bytes)?; + let decrypted_payload = Payload::deserialize(&decrypted_bytes) + .map_err(|e| OnionError::DeserializationError(e))?; let enc_payloads: Vec = self .enc_payloads @@ -59,11 +63,15 @@ impl Onion { let blinding_factor = calc_blinding_factor(&shared_secret, &self.ephemeral_pubkey)?; let mut ephemeral_pubkey = self.ephemeral_pubkey.clone(); - ephemeral_pubkey.mul_assign(&secp, &blinding_factor)?; + ephemeral_pubkey + .mul_assign(&secp, &blinding_factor) + .map_err(|e| OnionError::CalcPubKeyError(e))?; let mut commitment = self.commit.clone(); - commitment = secp::add_excess(&commitment, &decrypted_payload.excess)?; - commitment = secp::sub_value(&commitment, decrypted_payload.fee.into())?; + commitment = secp::add_excess(&commitment, &decrypted_payload.excess) + .map_err(|e| OnionError::CalcCommitError(e))?; + commitment = secp::sub_value(&commitment, decrypted_payload.fee.into()) + .map_err(|e| OnionError::CalcCommitError(e))?; let peeled_onion = Onion { ephemeral_pubkey, @@ -77,7 +85,7 @@ impl Onion { fn calc_blinding_factor( shared_secret: &SharedSecret, ephemeral_pubkey: &PublicKey, -) -> Result { +) -> Result { let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey, ProtocolVersion::local())?; let mut hasher = Sha256::default(); @@ -85,11 +93,12 @@ fn calc_blinding_factor( hasher.update(&shared_secret[0..32]); let secp = Secp256k1::new(); - let blind = SecretKey::from_slice(&secp, &hasher.finalize())?; + let blind = SecretKey::from_slice(&secp, &hasher.finalize()) + .map_err(|e| OnionError::CalcBlindError(e))?; Ok(blind) } -fn new_stream_cipher(shared_secret: &SharedSecret) -> Result { +fn new_stream_cipher(shared_secret: &SharedSecret) -> Result { let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?; mu_hmac.update(&shared_secret[0..32]); let mukey = mu_hmac.finalize().into_bytes(); @@ -101,7 +110,7 @@ fn new_stream_cipher(shared_secret: &SharedSecret) -> Result { } impl Writeable for Onion { - fn write(&self, writer: &mut W) -> std::result::Result<(), ser::Error> { + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { self.ephemeral_pubkey.write(writer)?; writer.write_fixed_bytes(&self.commit)?; writer.write_u64(self.enc_payloads.len() as u64)?; @@ -114,7 +123,7 @@ impl Writeable for Onion { } impl serde::ser::Serialize for Onion { - fn serialize(&self, serializer: S) -> std::result::Result + fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { @@ -134,7 +143,7 @@ impl serde::ser::Serialize for Onion { } impl<'de> serde::de::Deserialize<'de> for Onion { - fn deserialize(deserializer: D) -> std::result::Result + fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { @@ -155,7 +164,7 @@ impl<'de> serde::de::Deserialize<'de> for Onion { formatter.write_str("an Onion") } - fn visit_map(self, mut map: A) -> std::result::Result + fn visit_map(self, mut map: A) -> Result where A: serde::de::MapAccess<'de>, { @@ -207,10 +216,38 @@ impl<'de> serde::de::Deserialize<'de> for Onion { } } +/// Error types for creating and peeling Onions +#[derive(Clone, Error, Debug, PartialEq)] +pub enum OnionError { + #[error("Invalid key length for MAC initialization")] + InvalidKeyLength, + #[error("Serialization error occurred: {0:?}")] + SerializationError(ser::Error), + #[error("Deserialization error occurred: {0:?}")] + DeserializationError(ser::Error), + #[error("Error calculating blinding factor: {0:?}")] + CalcBlindError(secp256k1zkp::Error), + #[error("Error calculating ephemeral pubkey: {0:?}")] + CalcPubKeyError(secp256k1zkp::Error), + #[error("Error calculating commitment: {0:?}")] + CalcCommitError(secp256k1zkp::Error), +} + +impl From for OnionError { + fn from(_err: InvalidLength) -> OnionError { + InvalidKeyLength + } +} + +impl From for OnionError { + fn from(err: ser::Error) -> OnionError { + SerializationError(err) + } +} + #[cfg(test)] pub mod test_util { - use super::{Onion, RawBytes}; - use crate::error::Result; + use super::{Onion, OnionError, RawBytes}; use crate::secp::{Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret}; use crate::types::Payload; @@ -224,7 +261,7 @@ pub mod test_util { } /// Create an Onion for the Commitment, encrypting the payload for each hop - pub fn create_onion(commitment: &Commitment, hops: &Vec) -> Result { + pub fn create_onion(commitment: &Commitment, hops: &Vec) -> Result { let secp = Secp256k1::new(); let session_key = secp::random_secret(); let mut ephemeral_key = session_key.clone(); @@ -234,12 +271,15 @@ pub mod test_util { for hop in hops { let shared_secret = SharedSecret::new(&secp, &hop.pubkey, &ephemeral_key); - let ephemeral_pubkey = PublicKey::from_secret_key(&secp, &ephemeral_key)?; + let ephemeral_pubkey = PublicKey::from_secret_key(&secp, &ephemeral_key) + .map_err(|e| OnionError::CalcPubKeyError(e))?; let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?; shared_secrets.push(shared_secret); enc_payloads.push(hop.payload.serialize()?); - ephemeral_key.mul_assign(&secp, &blinding_factor)?; + ephemeral_key + .mul_assign(&secp, &blinding_factor) + .map_err(|e| OnionError::CalcPubKeyError(e))?; } for i in (0..shared_secrets.len()).rev() { @@ -250,7 +290,8 @@ pub mod test_util { } let onion = Onion { - ephemeral_pubkey: PublicKey::from_secret_key(&secp, &session_key)?, + ephemeral_pubkey: PublicKey::from_secret_key(&secp, &session_key) + .map_err(|e| OnionError::CalcPubKeyError(e))?, commit: commitment.clone(), enc_payloads, }; @@ -258,12 +299,17 @@ pub mod test_util { } /// Calculates the expected next ephemeral pubkey after peeling a layer off of the Onion. - pub fn next_ephemeral_pubkey(onion: &Onion, server_key: &SecretKey) -> Result { + pub fn next_ephemeral_pubkey( + onion: &Onion, + server_key: &SecretKey, + ) -> Result { let secp = Secp256k1::new(); let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone(); let shared_secret = SharedSecret::new(&secp, &ephemeral_pubkey, &server_key); let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?; - ephemeral_pubkey.mul_assign(&secp, &blinding_factor)?; + ephemeral_pubkey + .mul_assign(&secp, &blinding_factor) + .map_err(|e| OnionError::CalcPubKeyError(e))?; Ok(ephemeral_pubkey) } } diff --git a/src/secp.rs b/src/secp.rs index 8196c4c..db93c13 100644 --- a/src/secp.rs +++ b/src/secp.rs @@ -170,7 +170,10 @@ pub fn commit(value: u64, blind: &SecretKey) -> Result { } /// Add a blinding factor to an existing Commitment -pub fn add_excess(commitment: &Commitment, excess: &SecretKey) -> Result { +pub fn add_excess( + commitment: &Commitment, + excess: &SecretKey, +) -> std::result::Result { let secp = Secp256k1::with_caps(ContextFlag::Commit); let excess_commit: Commitment = secp.commit(0, excess.clone())?; @@ -180,7 +183,10 @@ pub fn add_excess(commitment: &Commitment, excess: &SecretKey) -> Result Result { +pub fn sub_value( + commitment: &Commitment, + value: u64, +) -> std::result::Result { let secp = Secp256k1::with_caps(ContextFlag::Commit); let neg_commit: Commitment = secp.commit(value, ZERO_KEY)?; let sum = secp.commit_sum(vec![commitment.clone()], vec![neg_commit.clone()])?; diff --git a/src/server.rs b/src/server.rs index ba20e9a..892bfd7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,6 @@ use crate::config::ServerConfig; use crate::node::{self, GrinNode}; -use crate::onion::Onion; +use crate::onion::{Onion, OnionError}; use crate::secp::{ComSignature, Commitment, RangeProof, Secp256k1, SecretKey}; use crate::wallet::{self, Wallet}; @@ -43,8 +43,8 @@ pub enum SwapError { CoinNotFound { commit: Commitment }, #[error("Output {commit:?} is already in the swap list.")] AlreadySwapped { commit: Commitment }, - #[error("Failed to peel onion layer: {0}")] - PeelOnionFailure(String), + #[error("Failed to peel onion layer: {0:?}")] + PeelOnionFailure(OnionError), #[error("Fee too low (expected >= {minimum_fee:?}, actual {actual_fee:?})")] FeeTooLow { minimum_fee: u64, actual_fee: u64 }, #[error("{0}")] @@ -126,7 +126,7 @@ impl Server for ServerImpl { let peeled = onion .peel_layer(&self.server_config.key) - .map_err(|e| SwapError::PeelOnionFailure(e.message()))?; + .map_err(|e| SwapError::PeelOnionFailure(e))?; // Verify the fee meets the minimum let fee: u64 = peeled.0.fee.into(); diff --git a/src/types.rs b/src/types.rs index 9a586f1..3a1d8a5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,3 @@ -use crate::error::{Result, StdResult}; use crate::secp::{self, RangeProof, SecretKey}; use grin_core::core::FeeFields; @@ -7,6 +6,7 @@ use serde::{Deserialize, Serialize}; const CURRENT_VERSION: u8 = 0; +// todo: Belongs in Onion #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Payload { pub excess: SecretKey, @@ -15,13 +15,13 @@ pub struct Payload { } impl Payload { - pub fn deserialize(bytes: &Vec) -> Result { + pub fn deserialize(bytes: &Vec) -> Result { let payload: Payload = ser::deserialize_default(&mut &bytes[..])?; Ok(payload) } #[cfg(test)] - pub fn serialize(&self) -> Result> { + pub fn serialize(&self) -> Result, ser::Error> { let mut vec = vec![]; ser::serialize_default(&mut vec, &self)?; Ok(vec) @@ -29,7 +29,7 @@ impl Payload { } impl Readable for Payload { - fn read(reader: &mut R) -> StdResult { + fn read(reader: &mut R) -> Result { let version = reader.read_u8()?; if version != CURRENT_VERSION { return Err(ser::Error::UnsupportedProtocolVersion); @@ -53,7 +53,7 @@ impl Readable for Payload { } impl Writeable for Payload { - fn write(&self, writer: &mut W) -> StdResult<(), ser::Error> { + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(CURRENT_VERSION)?; writer.write_fixed_bytes(&self.excess)?; writer.write_u64(self.fee.into())?;