Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

errors: Add DialError error and ListDialFailures event for better error reporting #206

Merged
merged 34 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b290160
error: Group error messages by functionality
lexnv Aug 12, 2024
d258898
crypto: Use NegotiationError instead of overarching Error
lexnv Aug 12, 2024
75fe484
tcp: Use SubstreamError instead of Error
lexnv Aug 12, 2024
86dcc6d
proto: Use SubstreamError in the TransportEvents
lexnv Aug 12, 2024
030889f
tcp/common: Use AddressError instead of Error
lexnv Aug 12, 2024
88da063
tcp/common: Add specific error for DNS lookup
lexnv Aug 12, 2024
d1cca42
tcp: Adopt new Dialerror
lexnv Aug 12, 2024
288cb58
crypto/ed25519: Return a less descriptive error to avoid leakage
lexnv Aug 13, 2024
8271d71
transport: Introduce `ListDialFailures` event for multi dial failures
lexnv Aug 13, 2024
2295001
transport: Untrack dial errors on the first connection success
lexnv Aug 13, 2024
b0d1cd9
websocket: Adjust to the new error intraface
lexnv Aug 13, 2024
412d270
quic: Adjust to the new error intraface
lexnv Aug 13, 2024
0a3089e
crypto/noise: Adjust webrtc path for errors
lexnv Aug 13, 2024
ed0826b
webrtc: Adjust to the new error intraface
lexnv Aug 13, 2024
dd45588
transport: Fix clippy
lexnv Aug 14, 2024
c9e7aff
tests: Adjust testing
lexnv Aug 14, 2024
409b2d8
error: Add documentation and cleanup variants
lexnv Aug 14, 2024
aed15ba
transport/tests: Check open failure propagates multiple errors
lexnv Aug 14, 2024
5ca073e
error: Fix typo
lexnv Aug 14, 2024
2dc3d5c
Update src/error.rs
lexnv Aug 21, 2024
b858c4a
Update src/error.rs
lexnv Aug 21, 2024
dcfe041
Update src/error.rs
lexnv Aug 21, 2024
de1044d
protocol: Use thiserr
lexnv Aug 21, 2024
57d271f
websocket: Trace after stream conversion
lexnv Aug 21, 2024
0e2c5a8
transport: Trace when the pending dial is not found
lexnv Aug 21, 2024
e6a4b98
error: Invalid address for protocol message
lexnv Aug 21, 2024
3f31972
dns: Ip version mismatch
lexnv Aug 21, 2024
6dec057
error: Cleanup from implementations
lexnv Aug 21, 2024
bb3ca5c
error: Use websocket wrapper error instead of from impl
lexnv Aug 21, 2024
adfc766
Fix clippy
lexnv Aug 21, 2024
c1d99c7
error: Remove invalid hash from parseError and use AddressError instead
lexnv Aug 21, 2024
9b42793
error: Address invalid multihash rename to invalid peerid
lexnv Aug 21, 2024
3696ab9
transport: Pending inbounds connections don't have a dial associated
lexnv Aug 21, 2024
47c10d8
Merge remote-tracking branch 'origin/master' into lexnv/impr-errors
lexnv Aug 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/crypto/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@

//! Ed25519 keys.

use crate::{error::Error, PeerId};
use crate::{
error::{Error, ParseError},
PeerId,
};

use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
use std::fmt;
Expand Down Expand Up @@ -131,11 +134,13 @@ impl PublicKey {

/// Try to parse a public key from a byte array containing the actual key as produced by
/// `to_bytes`.
pub fn try_from_bytes(k: &[u8]) -> crate::Result<PublicKey> {
let k = <[u8; 32]>::try_from(k)
.map_err(|e| Error::Other(format!("Failed to parse ed25519 public key: {e}")))?;
pub fn try_from_bytes(k: &[u8]) -> Result<PublicKey, ParseError> {
let k = <[u8; 32]>::try_from(k).map_err(|_| ParseError::InvalidPublicKey)?;

// The error type of the verifying key is deliberately opaque as to avoid side-channel
// leakage. We can't provide a more specific error type here.
ed25519::VerifyingKey::from_bytes(&k)
.map_err(|e| Error::Other(format!("Failed to parse ed25519 public key: {e}")))
.map_err(|_| ParseError::InvalidPublicKey)
.map(PublicKey)
}

Expand Down
24 changes: 10 additions & 14 deletions src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

//! Crypto-related code.

use crate::{error::*, peer_id::*};
use crate::{error::ParseError, peer_id::*};

pub mod ed25519;
pub(crate) mod noise;
Expand Down Expand Up @@ -65,11 +65,10 @@ impl PublicKey {

/// Decode a public key from a protobuf structure, e.g. read from storage
/// or received from another node.
pub fn from_protobuf_encoding(bytes: &[u8]) -> crate::Result<PublicKey> {
pub fn from_protobuf_encoding(bytes: &[u8]) -> Result<PublicKey, ParseError> {
use prost::Message;

let pubkey = keys_proto::PublicKey::decode(bytes)
.map_err(|error| Error::Other(format!("Invalid Protobuf: {error:?}")))?;
let pubkey = keys_proto::PublicKey::decode(bytes)?;

pubkey.try_into()
}
Expand All @@ -92,19 +91,16 @@ impl From<&PublicKey> for keys_proto::PublicKey {
}

impl TryFrom<keys_proto::PublicKey> for PublicKey {
type Error = Error;
type Error = ParseError;

fn try_from(pubkey: keys_proto::PublicKey) -> Result<Self, Self::Error> {
let key_type = keys_proto::KeyType::try_from(pubkey.r#type)
.map_err(|_| Error::Other(format!("Unknown key type: {}", pubkey.r#type)))?;

match key_type {
keys_proto::KeyType::Ed25519 =>
Ok(ed25519::PublicKey::try_from_bytes(&pubkey.data).map(PublicKey::Ed25519)?),
_ => Err(Error::Other(format!(
"Unsupported key type: {}",
key_type.as_str_name()
))),
.map_err(|_| ParseError::UnknownKeyType(pubkey.r#type))?;

if key_type == keys_proto::KeyType::Ed25519 {
Ok(ed25519::PublicKey::try_from_bytes(&pubkey.data).map(PublicKey::Ed25519)?)
} else {
Err(ParseError::UnknownKeyType(key_type as i32))
}
}
}
Expand Down
71 changes: 32 additions & 39 deletions src/crypto/noise/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
use crate::{
config::Role,
crypto::{ed25519::Keypair, PublicKey},
error, PeerId,
error::{NegotiationError, ParseError},
PeerId,
};

use bytes::{Buf, Bytes, BytesMut};
Expand Down Expand Up @@ -103,7 +104,7 @@ impl NoiseContext {
keypair: snow::Keypair,
id_keys: &Keypair,
role: Role,
) -> crate::Result<Self> {
) -> Result<Self, NegotiationError> {
let noise_payload = handshake_schema::NoiseHandshakePayload {
identity_key: Some(PublicKey::Ed25519(id_keys.public()).to_protobuf_encoding()),
identity_sig: Some(
Expand All @@ -113,7 +114,7 @@ impl NoiseContext {
};

let mut payload = Vec::with_capacity(noise_payload.encoded_len());
noise_payload.encode(&mut payload)?;
noise_payload.encode(&mut payload).map_err(ParseError::from)?;

Ok(Self {
noise: NoiseState::Handshake(noise),
Expand All @@ -123,7 +124,7 @@ impl NoiseContext {
})
}

pub fn new(keypair: &Keypair, role: Role) -> crate::Result<Self> {
pub fn new(keypair: &Keypair, role: Role) -> Result<Self, NegotiationError> {
tracing::trace!(target: LOG_TARGET, ?role, "create new noise configuration");

let builder: Builder<'_> = Builder::with_resolver(
Expand All @@ -144,7 +145,7 @@ impl NoiseContext {

/// Create new [`NoiseContext`] with prologue.
#[cfg(feature = "webrtc")]
pub fn with_prologue(id_keys: &Keypair, prologue: Vec<u8>) -> crate::Result<Self> {
pub fn with_prologue(id_keys: &Keypair, prologue: Vec<u8>) -> Result<Self, NegotiationError> {
let noise: Builder<'_> = Builder::with_resolver(
NOISE_PARAMETERS.parse().expect("qed; Valid noise pattern"),
Box::new(protocol::Resolver),
Expand All @@ -162,45 +163,44 @@ impl NoiseContext {

/// Get remote public key from the received Noise payload.
#[cfg(feature = "webrtc")]
pub fn get_remote_public_key(&mut self, reply: &[u8]) -> crate::Result<PublicKey> {
pub fn get_remote_public_key(&mut self, reply: &[u8]) -> Result<PublicKey, NegotiationError> {
let (len_slice, reply) = reply.split_at(2);
let len = u16::from_be_bytes(len_slice.try_into().map_err(|_| error::Error::InvalidData)?)
as usize;
let len = u16::from_be_bytes(
len_slice
.try_into()
.map_err(|_| NegotiationError::ParseError(ParseError::InvalidPublicKey))?,
) as usize;

let mut buffer = vec![0u8; len];

let NoiseState::Handshake(ref mut noise) = self.noise else {
tracing::error!(target: LOG_TARGET, "invalid state to read the second handshake message");
debug_assert!(false);
return Err(error::Error::Other(
"Noise state missmatch: expected handshake".into(),
));
return Err(NegotiationError::StateMismatch);
};

let res = noise.read_message(reply, &mut buffer)?;
buffer.truncate(res);

let payload = handshake_schema::NoiseHandshakePayload::decode(buffer.as_slice())?;
let payload = handshake_schema::NoiseHandshakePayload::decode(buffer.as_slice())
.map_err(|err| NegotiationError::ParseError(err.into()))?;

PublicKey::from_protobuf_encoding(&payload.identity_key.ok_or(
error::Error::NegotiationError(error::NegotiationError::PeerIdMissing),
)?)
let identity = payload.identity_key.ok_or(NegotiationError::PeerIdMissing)?;
PublicKey::from_protobuf_encoding(&identity).map_err(|err| err.into())
}

/// Get first message.
///
/// Listener only sends one message (the payload)
pub fn first_message(&mut self, role: Role) -> crate::Result<Vec<u8>> {
pub fn first_message(&mut self, role: Role) -> Result<Vec<u8>, NegotiationError> {
match role {
Role::Dialer => {
tracing::trace!(target: LOG_TARGET, "get noise dialer first message");

let NoiseState::Handshake(ref mut noise) = self.noise else {
tracing::error!(target: LOG_TARGET, "invalid state to read the first handshake message");
debug_assert!(false);
return Err(error::Error::Other(
"Noise state missmatch: expected handshake".into(),
));
return Err(NegotiationError::StateMismatch);
};

let mut buffer = vec![0u8; 256];
Expand All @@ -220,15 +220,13 @@ impl NoiseContext {
/// Get second message.
///
/// Only the dialer sends the second message.
pub fn second_message(&mut self) -> crate::Result<Vec<u8>> {
pub fn second_message(&mut self) -> Result<Vec<u8>, NegotiationError> {
tracing::trace!(target: LOG_TARGET, "get noise paylod message");

let NoiseState::Handshake(ref mut noise) = self.noise else {
tracing::error!(target: LOG_TARGET, "invalid state to read the first handshake message");
debug_assert!(false);
return Err(error::Error::Other(
"Noise state missmatch: expected handshake".into(),
));
return Err(NegotiationError::StateMismatch);
};

let mut buffer = vec![0u8; 2048];
Expand All @@ -246,7 +244,7 @@ impl NoiseContext {
async fn read_handshake_message<T: AsyncRead + AsyncWrite + Unpin>(
&mut self,
io: &mut T,
) -> crate::Result<Bytes> {
) -> Result<Bytes, NegotiationError> {
let mut size = BytesMut::zeroed(2);
io.read_exact(&mut size).await?;
let size = size.get_u16();
Expand All @@ -260,9 +258,7 @@ impl NoiseContext {
let NoiseState::Handshake(ref mut noise) = self.noise else {
tracing::error!(target: LOG_TARGET, "invalid state to read handshake message");
debug_assert!(false);
return Err(error::Error::Other(
"Noise state missmatch: expected handshake".into(),
));
return Err(NegotiationError::StateMismatch);
};

let nread = noise.read_message(&message, &mut out)?;
Expand All @@ -286,13 +282,10 @@ impl NoiseContext {
}

/// Convert Noise into transport mode.
fn into_transport(self) -> crate::Result<NoiseContext> {
fn into_transport(self) -> Result<NoiseContext, NegotiationError> {
let transport = match self.noise {
NoiseState::Handshake(noise) => noise.into_transport_mode()?,
NoiseState::Transport(_) =>
return Err(error::Error::Other(
"Noise state missmatch: expected handshake".into(),
)),
NoiseState::Transport(_) => return Err(NegotiationError::StateMismatch),
};

Ok(NoiseContext {
Expand Down Expand Up @@ -664,15 +657,15 @@ impl<S: AsyncRead + AsyncWrite + Unpin> AsyncWrite for NoiseSocket<S> {
}

/// Try to parse `PeerId` from received `NoiseHandshakePayload`
fn parse_peer_id(buf: &[u8]) -> crate::Result<PeerId> {
fn parse_peer_id(buf: &[u8]) -> Result<PeerId, NegotiationError> {
match handshake_schema::NoiseHandshakePayload::decode(buf) {
Ok(payload) => {
let public_key = PublicKey::from_protobuf_encoding(&payload.identity_key.ok_or(
error::Error::NegotiationError(error::NegotiationError::PeerIdMissing),
)?)?;
let identity = payload.identity_key.ok_or(NegotiationError::PeerIdMissing)?;

let public_key = PublicKey::from_protobuf_encoding(&identity)?;
Ok(PeerId::from_public_key(&public_key))
}
Err(err) => Err(From::from(err)),
Err(err) => Err(ParseError::from(err).into()),
}
}

Expand All @@ -683,7 +676,7 @@ pub async fn handshake<S: AsyncRead + AsyncWrite + Unpin>(
role: Role,
max_read_ahead_factor: usize,
max_write_buffer_size: usize,
) -> crate::Result<(NoiseSocket<S>, PeerId)> {
) -> Result<(NoiseSocket<S>, PeerId), NegotiationError> {
tracing::debug!(target: LOG_TARGET, ?role, "start noise handshake");

let mut noise = NoiseContext::new(keypair, role)?;
Expand Down Expand Up @@ -797,7 +790,7 @@ mod tests {
#[test]
fn invalid_peer_id_schema() {
match parse_peer_id(&vec![1, 2, 3, 4]).unwrap_err() {
crate::Error::ParseError(_) => {}
NegotiationError::ParseError(_) => {}
_ => panic!("invalid error"),
}
}
Expand Down
Loading