switch to thiserror for onion

This commit is contained in:
scilio 2022-08-22 10:59:41 -04:00
parent 92825e884d
commit fdbf25c965
5 changed files with 85 additions and 49 deletions

View file

@ -10,7 +10,6 @@ pub struct Error {
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
pub type StdResult<T, E> = std::result::Result<T, E>;
/// MWixnet error types /// MWixnet error types
#[derive(Clone, Debug, Eq, Fail, PartialEq)] #[derive(Clone, Debug, Eq, Fail, PartialEq)]
@ -18,9 +17,6 @@ pub enum ErrorKind {
/// Error from secp256k1-zkp library /// Error from secp256k1-zkp library
#[fail(display = "Secp Error")] #[fail(display = "Secp Error")]
SecpError, SecpError,
/// Invalid key length for MAC initialization
#[fail(display = "InvalidKeyLength")]
InvalidKeyLength,
/// Wraps an io error produced when reading or writing /// Wraps an io error produced when reading or writing
#[fail(display = "IO Error: {}", _0)] #[fail(display = "IO Error: {}", _0)]
IOErr(String, io::ErrorKind), IOErr(String, io::ErrorKind),
@ -78,10 +74,6 @@ impl Error {
inner: Context::new(kind), inner: Context::new(kind),
} }
} }
pub fn message(&self) -> String {
format!("{}", self).into()
}
} }
impl From<ErrorKind> for Error { impl From<ErrorKind> for Error {
@ -106,14 +98,6 @@ impl From<secp256k1zkp::Error> for Error {
} }
} }
impl From<hmac::digest::InvalidLength> for Error {
fn from(_error: hmac::digest::InvalidLength) -> Error {
Error {
inner: Context::new(ErrorKind::InvalidKeyLength),
}
}
}
impl From<grin_api::Error> for Error { impl From<grin_api::Error> for Error {
fn from(e: grin_api::Error) -> Error { fn from(e: grin_api::Error) -> Error {
Error { Error {

View file

@ -1,16 +1,19 @@
use crate::error::Result;
use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret}; use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret};
use crate::types::Payload; use crate::types::Payload;
use crate::onion::OnionError::{InvalidKeyLength, SerializationError};
use chacha20::cipher::{NewCipher, StreamCipher}; use chacha20::cipher::{NewCipher, StreamCipher};
use chacha20::{ChaCha20, Key, Nonce}; use chacha20::{ChaCha20, Key, Nonce};
use grin_core::ser::{self, ProtocolVersion, Writeable, Writer}; use grin_core::ser::{self, ProtocolVersion, Writeable, Writer};
use grin_util::{self, ToHex}; use grin_util::{self, ToHex};
use hmac::digest::InvalidLength;
use hmac::{Hmac, Mac}; use hmac::{Hmac, Mac};
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use serde::Deserialize; use serde::Deserialize;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::fmt; use std::fmt;
use std::result::Result;
use thiserror::Error;
type HmacSha256 = Hmac<Sha256>; type HmacSha256 = Hmac<Sha256>;
type RawBytes = Vec<u8>; type RawBytes = Vec<u8>;
@ -27,14 +30,14 @@ pub struct Onion {
} }
impl Onion { impl Onion {
pub fn serialize(&self) -> Result<Vec<u8>> { pub fn serialize(&self) -> Result<Vec<u8>, ser::Error> {
let mut vec = vec![]; let mut vec = vec![];
ser::serialize_default(&mut vec, &self)?; ser::serialize_default(&mut vec, &self)?;
Ok(vec) Ok(vec)
} }
/// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload /// 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 secp = Secp256k1::new();
let shared_secret = SharedSecret::new(&secp, &self.ephemeral_pubkey, &secret_key); 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(); let mut decrypted_bytes = self.enc_payloads[0].clone();
cipher.apply_keystream(&mut decrypted_bytes); 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<RawBytes> = self let enc_payloads: Vec<RawBytes> = self
.enc_payloads .enc_payloads
@ -59,11 +63,15 @@ impl Onion {
let blinding_factor = calc_blinding_factor(&shared_secret, &self.ephemeral_pubkey)?; let blinding_factor = calc_blinding_factor(&shared_secret, &self.ephemeral_pubkey)?;
let mut ephemeral_pubkey = self.ephemeral_pubkey.clone(); 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(); let mut commitment = self.commit.clone();
commitment = secp::add_excess(&commitment, &decrypted_payload.excess)?; commitment = secp::add_excess(&commitment, &decrypted_payload.excess)
commitment = secp::sub_value(&commitment, decrypted_payload.fee.into())?; .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 { let peeled_onion = Onion {
ephemeral_pubkey, ephemeral_pubkey,
@ -77,7 +85,7 @@ impl Onion {
fn calc_blinding_factor( fn calc_blinding_factor(
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
ephemeral_pubkey: &PublicKey, ephemeral_pubkey: &PublicKey,
) -> Result<SecretKey> { ) -> Result<SecretKey, OnionError> {
let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey, ProtocolVersion::local())?; let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey, ProtocolVersion::local())?;
let mut hasher = Sha256::default(); let mut hasher = Sha256::default();
@ -85,11 +93,12 @@ fn calc_blinding_factor(
hasher.update(&shared_secret[0..32]); hasher.update(&shared_secret[0..32]);
let secp = Secp256k1::new(); 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) Ok(blind)
} }
fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20> { fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20, OnionError> {
let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?; let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?;
mu_hmac.update(&shared_secret[0..32]); mu_hmac.update(&shared_secret[0..32]);
let mukey = mu_hmac.finalize().into_bytes(); let mukey = mu_hmac.finalize().into_bytes();
@ -101,7 +110,7 @@ fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20> {
} }
impl Writeable for Onion { impl Writeable for Onion {
fn write<W: Writer>(&self, writer: &mut W) -> std::result::Result<(), ser::Error> { fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.ephemeral_pubkey.write(writer)?; self.ephemeral_pubkey.write(writer)?;
writer.write_fixed_bytes(&self.commit)?; writer.write_fixed_bytes(&self.commit)?;
writer.write_u64(self.enc_payloads.len() as u64)?; writer.write_u64(self.enc_payloads.len() as u64)?;
@ -114,7 +123,7 @@ impl Writeable for Onion {
} }
impl serde::ser::Serialize for Onion { impl serde::ser::Serialize for Onion {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: serde::ser::Serializer, S: serde::ser::Serializer,
{ {
@ -134,7 +143,7 @@ impl serde::ser::Serialize for Onion {
} }
impl<'de> serde::de::Deserialize<'de> for Onion { impl<'de> serde::de::Deserialize<'de> for Onion {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: serde::de::Deserializer<'de>, D: serde::de::Deserializer<'de>,
{ {
@ -155,7 +164,7 @@ impl<'de> serde::de::Deserialize<'de> for Onion {
formatter.write_str("an Onion") formatter.write_str("an Onion")
} }
fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error> fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where where
A: serde::de::MapAccess<'de>, 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<InvalidLength> for OnionError {
fn from(_err: InvalidLength) -> OnionError {
InvalidKeyLength
}
}
impl From<ser::Error> for OnionError {
fn from(err: ser::Error) -> OnionError {
SerializationError(err)
}
}
#[cfg(test)] #[cfg(test)]
pub mod test_util { pub mod test_util {
use super::{Onion, RawBytes}; use super::{Onion, OnionError, RawBytes};
use crate::error::Result;
use crate::secp::{Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret}; use crate::secp::{Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret};
use crate::types::Payload; use crate::types::Payload;
@ -224,7 +261,7 @@ pub mod test_util {
} }
/// Create an Onion for the Commitment, encrypting the payload for each hop /// Create an Onion for the Commitment, encrypting the payload for each hop
pub fn create_onion(commitment: &Commitment, hops: &Vec<Hop>) -> Result<Onion> { pub fn create_onion(commitment: &Commitment, hops: &Vec<Hop>) -> Result<Onion, OnionError> {
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let session_key = secp::random_secret(); let session_key = secp::random_secret();
let mut ephemeral_key = session_key.clone(); let mut ephemeral_key = session_key.clone();
@ -234,12 +271,15 @@ pub mod test_util {
for hop in hops { for hop in hops {
let shared_secret = SharedSecret::new(&secp, &hop.pubkey, &ephemeral_key); 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)?; let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?;
shared_secrets.push(shared_secret); shared_secrets.push(shared_secret);
enc_payloads.push(hop.payload.serialize()?); 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() { for i in (0..shared_secrets.len()).rev() {
@ -250,7 +290,8 @@ pub mod test_util {
} }
let onion = Onion { 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(), commit: commitment.clone(),
enc_payloads, enc_payloads,
}; };
@ -258,12 +299,17 @@ pub mod test_util {
} }
/// Calculates the expected next ephemeral pubkey after peeling a layer off of the Onion. /// 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<PublicKey> { pub fn next_ephemeral_pubkey(
onion: &Onion,
server_key: &SecretKey,
) -> Result<PublicKey, OnionError> {
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone(); let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone();
let shared_secret = SharedSecret::new(&secp, &ephemeral_pubkey, &server_key); let shared_secret = SharedSecret::new(&secp, &ephemeral_pubkey, &server_key);
let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?; 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) Ok(ephemeral_pubkey)
} }
} }

View file

@ -170,7 +170,10 @@ pub fn commit(value: u64, blind: &SecretKey) -> Result<Commitment> {
} }
/// Add a blinding factor to an existing Commitment /// Add a blinding factor to an existing Commitment
pub fn add_excess(commitment: &Commitment, excess: &SecretKey) -> Result<Commitment> { pub fn add_excess(
commitment: &Commitment,
excess: &SecretKey,
) -> std::result::Result<Commitment, secp256k1zkp::Error> {
let secp = Secp256k1::with_caps(ContextFlag::Commit); let secp = Secp256k1::with_caps(ContextFlag::Commit);
let excess_commit: Commitment = secp.commit(0, excess.clone())?; let excess_commit: Commitment = secp.commit(0, excess.clone())?;
@ -180,7 +183,10 @@ pub fn add_excess(commitment: &Commitment, excess: &SecretKey) -> Result<Commitm
} }
/// Subtracts a value (v*H) from an existing commitment /// Subtracts a value (v*H) from an existing commitment
pub fn sub_value(commitment: &Commitment, value: u64) -> Result<Commitment> { pub fn sub_value(
commitment: &Commitment,
value: u64,
) -> std::result::Result<Commitment, secp256k1zkp::Error> {
let secp = Secp256k1::with_caps(ContextFlag::Commit); let secp = Secp256k1::with_caps(ContextFlag::Commit);
let neg_commit: Commitment = secp.commit(value, ZERO_KEY)?; let neg_commit: Commitment = secp.commit(value, ZERO_KEY)?;
let sum = secp.commit_sum(vec![commitment.clone()], vec![neg_commit.clone()])?; let sum = secp.commit_sum(vec![commitment.clone()], vec![neg_commit.clone()])?;

View file

@ -1,6 +1,6 @@
use crate::config::ServerConfig; use crate::config::ServerConfig;
use crate::node::{self, GrinNode}; use crate::node::{self, GrinNode};
use crate::onion::Onion; use crate::onion::{Onion, OnionError};
use crate::secp::{ComSignature, Commitment, RangeProof, Secp256k1, SecretKey}; use crate::secp::{ComSignature, Commitment, RangeProof, Secp256k1, SecretKey};
use crate::wallet::{self, Wallet}; use crate::wallet::{self, Wallet};
@ -43,8 +43,8 @@ pub enum SwapError {
CoinNotFound { commit: Commitment }, CoinNotFound { commit: Commitment },
#[error("Output {commit:?} is already in the swap list.")] #[error("Output {commit:?} is already in the swap list.")]
AlreadySwapped { commit: Commitment }, AlreadySwapped { commit: Commitment },
#[error("Failed to peel onion layer: {0}")] #[error("Failed to peel onion layer: {0:?}")]
PeelOnionFailure(String), PeelOnionFailure(OnionError),
#[error("Fee too low (expected >= {minimum_fee:?}, actual {actual_fee:?})")] #[error("Fee too low (expected >= {minimum_fee:?}, actual {actual_fee:?})")]
FeeTooLow { minimum_fee: u64, actual_fee: u64 }, FeeTooLow { minimum_fee: u64, actual_fee: u64 },
#[error("{0}")] #[error("{0}")]
@ -126,7 +126,7 @@ impl Server for ServerImpl {
let peeled = onion let peeled = onion
.peel_layer(&self.server_config.key) .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 // Verify the fee meets the minimum
let fee: u64 = peeled.0.fee.into(); let fee: u64 = peeled.0.fee.into();

View file

@ -1,4 +1,3 @@
use crate::error::{Result, StdResult};
use crate::secp::{self, RangeProof, SecretKey}; use crate::secp::{self, RangeProof, SecretKey};
use grin_core::core::FeeFields; use grin_core::core::FeeFields;
@ -7,6 +6,7 @@ use serde::{Deserialize, Serialize};
const CURRENT_VERSION: u8 = 0; const CURRENT_VERSION: u8 = 0;
// todo: Belongs in Onion
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Payload { pub struct Payload {
pub excess: SecretKey, pub excess: SecretKey,
@ -15,13 +15,13 @@ pub struct Payload {
} }
impl Payload { impl Payload {
pub fn deserialize(bytes: &Vec<u8>) -> Result<Payload> { pub fn deserialize(bytes: &Vec<u8>) -> Result<Payload, ser::Error> {
let payload: Payload = ser::deserialize_default(&mut &bytes[..])?; let payload: Payload = ser::deserialize_default(&mut &bytes[..])?;
Ok(payload) Ok(payload)
} }
#[cfg(test)] #[cfg(test)]
pub fn serialize(&self) -> Result<Vec<u8>> { pub fn serialize(&self) -> Result<Vec<u8>, ser::Error> {
let mut vec = vec![]; let mut vec = vec![];
ser::serialize_default(&mut vec, &self)?; ser::serialize_default(&mut vec, &self)?;
Ok(vec) Ok(vec)
@ -29,7 +29,7 @@ impl Payload {
} }
impl Readable for Payload { impl Readable for Payload {
fn read<R: Reader>(reader: &mut R) -> StdResult<Payload, ser::Error> { fn read<R: Reader>(reader: &mut R) -> Result<Payload, ser::Error> {
let version = reader.read_u8()?; let version = reader.read_u8()?;
if version != CURRENT_VERSION { if version != CURRENT_VERSION {
return Err(ser::Error::UnsupportedProtocolVersion); return Err(ser::Error::UnsupportedProtocolVersion);
@ -53,7 +53,7 @@ impl Readable for Payload {
} }
impl Writeable for Payload { impl Writeable for Payload {
fn write<W: Writer>(&self, writer: &mut W) -> StdResult<(), ser::Error> { fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u8(CURRENT_VERSION)?; writer.write_u8(CURRENT_VERSION)?;
writer.write_fixed_bytes(&self.excess)?; writer.write_fixed_bytes(&self.excess)?;
writer.write_u64(self.fee.into())?; writer.write_u64(self.fee.into())?;