use std::fmt;
use rsa::pkcs1;
use crate::codes::{disconnect, open};
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(thiserror::Error, Debug)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum Error {
#[error("cryptography error: {0}")]
Crypto(&'static str),
#[error("randomness error: {0}")]
Random(&'static str),
#[error("mac verification failed")]
Mac,
#[error("signature verification failed")]
Signature,
#[error("algorithm is not compatible with this public key format")]
PubkeyFormat,
#[error("algorithm is not compatible with this private key format")]
PrivkeyFormat,
#[error("server public key was not accepted")]
PubkeyAccept(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("protocol error: {0}")]
Protocol(&'static str),
#[error("could not decode bytes: {0}")]
Decode(&'static str),
#[error("could not negotiate algorithm")]
AlgoNegotiate(#[source] AlgoNegotiateError),
#[error("we do not implement packet {0}")]
PacketNotImplemented(u8),
#[error("authentication method was aborted")]
AuthAborted,
#[error("another authentication method is pending")]
AuthPending,
#[error("authentication failed")]
AuthFailed,
#[error("channel is closed")]
ChannelClosed,
#[error("could not open channel")]
ChannelOpen(#[source] ChannelOpenError),
#[error("channel request failed")]
ChannelReq,
#[error("global request failed")]
GlobalReq,
#[error("rekeying was aborted")]
RekeyAborted,
#[error("rekeying was rejected by the peer")]
RekeyRejected,
#[error("IO error when reading")]
ReadIo(#[source] std::io::Error),
#[error("IO error when writing")]
WriteIo(#[source] std::io::Error),
#[error("peer did not recognize our packet with seq {0}")]
PeerRejectedPacket(u32),
#[error("connection unexpectedly closed by peer")]
PeerClosed,
#[error("peer disconnected")]
PeerDisconnected(#[source] DisconnectError),
#[error("client is closed")]
ClientClosed,
#[error("client has already disconnected")]
ClientDisconnected,
#[error("could not parse PEM file")]
Pem(#[source] pem::PemError),
#[error("could not parse file in PKCS#1 format")]
Pkcs1(#[source] pkcs1::Error),
#[error("could not parse file in PKCS#8 format: {0}")]
Pkcs8(pkcs8::Error), #[error("could not parse file in PKCS#8 format (RSA)")]
Pkcs8Rsa(#[source] rsa::pkcs8::Error),
#[error("could not parse file in PKCS#8 format (SPKI): {0}")]
Pkcs8Spki(pkcs8::spki::Error), #[error("could not parse file in PKCS#8 format (RSA/SPKI)")]
Pkcs8RsaSpki(#[source] rsa::pkcs8::spki::Error),
#[error("could not parse file in PKCS#8 format (Ed25519)")]
Pkcs8Ed25519(#[source] ed25519_dalek::SignatureError),
#[error("unknown algorithm OID {0:?} in PKCS#8 file")]
Pkcs8BadAlgorithmOid(String),
#[error("unknown curve OID {0:?} in PKCS#8 file")]
Pkcs8BadCurveOid(String),
#[error("unexpected PEM tag {0:?}, expected {1:?}")]
BadPemTag(String, String),
#[error("unsupported PEM tag {0:?}")]
UnknownPemTag(String),
#[error("bad passphrase when decoding key")]
BadKeyPassphrase,
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("for {algo_name:}, our algos are {our_algos:?}, their algos are {their_algos:?}")]
pub struct AlgoNegotiateError {
pub algo_name: String,
pub our_algos: Vec<String>,
pub their_algos: Vec<String>,
}
#[derive(Debug, Clone, thiserror::Error)]
pub struct DisconnectError {
pub reason_code: u32,
pub description: String,
pub description_lang: String,
}
impl DisconnectError {
pub fn reason_to_str(&self) -> Option<&'static str> {
disconnect::to_str(self.reason_code)
}
pub fn by_app() -> DisconnectError {
DisconnectError {
reason_code: disconnect::BY_APPLICATION,
description: "disconnected by application".into(),
description_lang: "".into(),
}
}
}
impl fmt::Display for DisconnectError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_reason(f, disconnect::to_str(self.reason_code), self.reason_code, &self.description)
}
}
#[derive(Debug, Clone, thiserror::Error)]
pub struct ChannelOpenError {
pub reason_code: u32,
pub description: String,
pub description_lang: String,
}
impl fmt::Display for ChannelOpenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_reason(f, open::to_str(self.reason_code), self.reason_code, &self.description)
}
}
fn fmt_reason(
f: &mut fmt::Formatter,
reason: Option<&'static str>,
reason_code: u32,
description: &str,
) -> fmt::Result {
write!(f, "server returned error ")?;
if let Some(reason) = reason {
write!(f, "`{}` ({})", reason, reason_code)?;
} else {
write!(f, "{}", reason_code)?;
}
if !description.is_empty() {
write!(f, ": {:?}", description)?;
}
Ok(())
}