remove anyhow

This is the first of a couple of commits aiming to completely remove anyhow in favor of using a mixture of `log` and a `thiserror` based enum. Partially fixes #93.
This commit is contained in:
wucke13
2023-06-17 22:43:45 +02:00
parent 5bebdd9284
commit 63ff75b93c
11 changed files with 367 additions and 302 deletions

59
Cargo.lock generated
View File

@@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
@@ -92,9 +83,6 @@ name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
dependencies = [
"backtrace",
]
[[package]]
name = "atty"
@@ -113,21 +101,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide 0.6.2",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.1"
@@ -496,7 +469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
dependencies = [
"crc32fast",
"miniz_oxide 0.7.1",
"miniz_oxide",
]
[[package]]
@@ -508,12 +481,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "gimli"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "glob"
version = "0.3.1"
@@ -751,15 +718,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@@ -810,15 +768,6 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@@ -1011,12 +960,6 @@ dependencies = [
"toml",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "1.1.0"

View File

@@ -14,7 +14,6 @@ name = "handshake"
harness = false
[dependencies]
anyhow = { version = "1.0.71", features = ["backtrace"] }
base64 = "0.21.1"
static_assertions = "1.1.0"
memoffset = "0.9.0"

View File

@@ -1,6 +1,3 @@
use anyhow::bail;
use anyhow::Result;
use log::{error, info, warn};
use mio::Interest;
use mio::Token;
@@ -22,10 +19,12 @@ use std::slice;
use std::time::Duration;
use crate::util::fopen_w;
use crate::RosenpassError;
use crate::{
config::Verbosity,
protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing},
util::{b64_writer, fmt_b64},
Result,
};
const IPV4_ANY_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
@@ -98,7 +97,7 @@ impl SocketPtr {
&mut srv.sockets[self.0]
}
pub fn send_to(&self, srv: &AppServer, buf: &[u8], addr: SocketAddr) -> anyhow::Result<()> {
pub fn send_to(&self, srv: &AppServer, buf: &[u8], addr: SocketAddr) -> Result<()> {
self.get(srv).send_to(&buf, addr)?;
Ok(())
}
@@ -181,7 +180,7 @@ impl Endpoint {
}
/// Start endpoint discovery from a hostname
pub fn discovery_from_hostname(hostname: String) -> anyhow::Result<Self> {
pub fn discovery_from_hostname(hostname: String) -> Result<Self> {
let host = HostPathDiscoveryEndpoint::lookup(hostname)?;
Ok(Endpoint::Discovery(host))
}
@@ -211,7 +210,7 @@ impl Endpoint {
Some(Self::discovery_from_addresses(addrs))
}
pub fn send(&self, srv: &AppServer, buf: &[u8]) -> anyhow::Result<()> {
pub fn send(&self, srv: &AppServer, buf: &[u8]) -> Result<()> {
use Endpoint::*;
match self {
SocketBoundAddress { socket, addr } => socket.send_to(srv, buf, *addr),
@@ -270,7 +269,7 @@ impl HostPathDiscoveryEndpoint {
}
/// Lookup a hostname
pub fn lookup(hostname: String) -> anyhow::Result<Self> {
pub fn lookup(hostname: String) -> Result<Self> {
Ok(Self {
addresses: ToSocketAddrs::to_socket_addrs(&hostname)?.collect(),
scouting_state: Cell::new((0, 0)),
@@ -291,7 +290,7 @@ impl HostPathDiscoveryEndpoint {
/// Attempt to reach the host
///
/// Will round-robin-try different socket-ip-combinations on each call.
pub fn send_scouting(&self, srv: &AppServer, buf: &[u8]) -> anyhow::Result<()> {
pub fn send_scouting(&self, srv: &AppServer, buf: &[u8]) -> Result<()> {
let (addr_off, sock_off) = self.scouting_state.get();
let mut addrs = (&self.addresses)
@@ -330,23 +329,19 @@ impl HostPathDiscoveryEndpoint {
}
}
bail!("Unable to send message: All sockets returned errors.")
error!("Unable to send message: All sockets returned errors.");
return Err(RosenpassError::RuntimeError);
}
}
impl AppServer {
pub fn new(
sk: SSk,
pk: SPk,
addrs: Vec<SocketAddr>,
verbosity: Verbosity,
) -> anyhow::Result<Self> {
pub fn new(sk: SSk, pk: SPk, addrs: Vec<SocketAddr>, verbosity: Verbosity) -> Result<Self> {
// setup mio
let mio_poll = mio::Poll::new()?;
let events = mio::Events::with_capacity(8);
// bind each SocketAddr to a socket
let maybe_sockets: Result<Vec<_>, _> =
let maybe_sockets: std::result::Result<Vec<_>, std::io::Error> =
addrs.into_iter().map(mio::net::UdpSocket::bind).collect();
let mut sockets = maybe_sockets?;
@@ -413,7 +408,8 @@ impl AppServer {
}
if sockets.is_empty() {
bail!("No sockets to listen on!")
error!("No sockets to listen on!");
return Err(RosenpassError::RuntimeError);
}
// register all sockets to mio
@@ -447,7 +443,7 @@ impl AppServer {
outfile: Option<PathBuf>,
outwg: Option<WireguardOut>,
hostname: Option<String>,
) -> anyhow::Result<AppPeerPtr> {
) -> Result<AppPeerPtr> {
let PeerPtr(pn) = self.crypt.add_peer(psk, pk)?;
assert!(pn == self.peers.len());
let initial_endpoint = hostname
@@ -463,7 +459,7 @@ impl AppServer {
Ok(AppPeerPtr(pn))
}
pub fn listen_loop(&mut self) -> anyhow::Result<()> {
pub fn listen_loop(&mut self) -> Result<()> {
const INIT_SLEEP: f64 = 0.01;
const MAX_FAILURES: i32 = 10;
let mut failure_cnt = 0;
@@ -484,10 +480,11 @@ impl AppServer {
let sleep = INIT_SLEEP * 2.0f64.powf(f64::from(failure_cnt - 1));
let tries_left = MAX_FAILURES - (failure_cnt - 1);
error!(
"unexpected error after processing {} messages: {:?} {}",
"unexpected error after processing {} messages: {:?}",
msgs_processed,
err,
err.backtrace()
// TODO do we need backtraces?
// err.backtrace()
);
if tries_left > 0 {
error!("re-initializing networking in {sleep}! {tries_left} tries left.");
@@ -495,11 +492,12 @@ impl AppServer {
continue;
}
bail!("too many network failures");
error!("too many network failures");
return Err(RosenpassError::RuntimeError);
}
}
pub fn event_loop(&mut self) -> anyhow::Result<()> {
pub fn event_loop(&mut self) -> Result<()> {
let (mut rx, mut tx) = (MsgBuf::zero(), MsgBuf::zero());
/// if socket address for peer is known, call closure
@@ -548,11 +546,9 @@ impl AppServer {
match self.crypt.handle_msg(&rx[..len], &mut *tx) {
Err(ref e) => {
self.verbose().then(|| {
info!(
"error processing incoming message from {:?}: {:?} {}",
endpoint,
e,
e.backtrace()
error!(
"error processing incoming message from {:?}: {:?}",
endpoint, e
);
});
}
@@ -580,12 +576,7 @@ impl AppServer {
}
}
pub fn output_key(
&self,
peer: AppPeerPtr,
why: KeyOutputReason,
key: &SymKey,
) -> anyhow::Result<()> {
pub fn output_key(&self, peer: AppPeerPtr, why: KeyOutputReason, key: &SymKey) -> Result<()> {
let peerid = peer.lower().get(&self.crypt).pidt()?;
let ap = peer.get_app(self);
@@ -636,7 +627,7 @@ impl AppServer {
Ok(())
}
pub fn poll(&mut self, rx_buf: &mut [u8]) -> anyhow::Result<AppPollResult> {
pub fn poll(&mut self, rx_buf: &mut [u8]) -> Result<AppPollResult> {
use crate::protocol::PollResult as C;
use AppPollResult as A;
loop {
@@ -661,7 +652,7 @@ impl AppServer {
&mut self,
buf: &mut [u8],
timeout: Timing,
) -> anyhow::Result<Option<(usize, Endpoint)>> {
) -> Result<Option<(usize, Endpoint)>> {
let timeout = Duration::from_secs_f64(timeout);
// if there is no time to wait on IO, well, then, lets not waste any time!

View File

@@ -1,7 +1,3 @@
use anyhow::{bail, ensure};
use clap::Parser;
use std::path::{Path, PathBuf};
use crate::app_server;
use crate::app_server::AppServer;
use crate::util::{LoadValue, LoadValueB64};
@@ -10,7 +6,12 @@ use crate::{
coloring::Secret,
pqkem::{StaticKEM, KEM},
protocol::{SPk, SSk, SymKey},
Result,
RosenpassError,
};
use clap::Parser;
use log::error;
use std::path::{Path, PathBuf};
use super::config;
@@ -98,7 +99,7 @@ pub enum Cli {
}
impl Cli {
pub fn run() -> anyhow::Result<()> {
pub fn run() -> Result<()> {
let cli = Self::parse();
use Cli::*;
@@ -109,10 +110,10 @@ impl Cli {
.status();
}
GenConfig { config_file, force } => {
ensure!(
force || !config_file.exists(),
"config file {config_file:?} already exists"
);
if !force && config_file.exists() {
error!("config file {config_file:?} already exists");
return Err(RosenpassError::RuntimeError);
}
config::Rosenpass::example_config().store(config_file)?;
}
@@ -126,35 +127,34 @@ impl Cli {
// figure out where the key file is specified, in the config file or directly as flag?
let (pkf, skf) = match (config_file, public_key, secret_key) {
(Some(config_file), _, _) => {
ensure!(
config_file.exists(),
"config file {config_file:?} does not exist"
);
if !config_file.exists() {
error!("config file {config_file:?} does not exist");
return Err(RosenpassError::RuntimeError);
}
let config = config::Rosenpass::load(config_file)?;
(config.public_key, config.secret_key)
}
(_, Some(pkf), Some(skf)) => (pkf, skf),
_ => {
bail!("either a config-file or both public-key and secret-key file are required")
}
_ => return Err(RosenpassError::ConfigError(
"either a config-file or both public-key and secret-key file are required"
.into(),
)),
};
// check that we are not overriding something unintentionally
let mut problems = vec![];
let mut problems = false;
if !force && pkf.is_file() {
problems.push(format!(
"public-key file {pkf:?} exist, refusing to overwrite it"
));
problems = true;
error!("public-key file {pkf:?} exist, refusing to overwrite it");
}
if !force && skf.is_file() {
problems.push(format!(
"secret-key file {skf:?} exist, refusing to overwrite it"
));
problems = true;
error!("secret-key file {skf:?} exist, refusing to overwrite it");
}
if !problems.is_empty() {
bail!(problems.join("\n"));
if problems {
return Err(RosenpassError::RuntimeError);
}
// generate the keys and store them in files
@@ -169,10 +169,10 @@ impl Cli {
}
ExchangeConfig { config_file } => {
ensure!(
config_file.exists(),
"config file '{config_file:?}' does not exist"
);
if !config_file.exists() {
error!("config file '{config_file:?}' does not exist");
return Err(RosenpassError::RuntimeError);
}
let config = config::Rosenpass::load(config_file)?;
config.validate()?;
@@ -215,7 +215,7 @@ impl Cli {
Ok(())
}
fn event_loop(config: config::Rosenpass) -> anyhow::Result<()> {
fn event_loop(config: config::Rosenpass) -> Result<()> {
// load own keys
let sk = SSk::load(&config.secret_key)?;
let pk = SPk::load(&config.public_key)?;
@@ -248,11 +248,11 @@ impl Cli {
}
trait StoreSecret {
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()>;
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()>;
}
impl<const N: usize> StoreSecret for Secret<N> {
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()> {
std::fs::write(path, self.secret())?;
Ok(())
}

View File

@@ -6,10 +6,10 @@ use std::{
path::{Path, PathBuf},
};
use anyhow::{bail, ensure};
use log::{error, warn};
use serde::{Deserialize, Serialize};
use crate::util::fopen_w;
use crate::{util::fopen_w, Result, RosenpassError};
#[derive(Debug, Serialize, Deserialize)]
pub struct Rosenpass {
@@ -62,7 +62,7 @@ impl Rosenpass {
/// Load a config file from a file path
///
/// no validation is conducted
pub fn load<P: AsRef<Path>>(p: P) -> anyhow::Result<Self> {
pub fn load<P: AsRef<Path>>(p: P) -> Result<Self> {
let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?;
config.config_file_path = p.as_ref().to_owned();
@@ -70,7 +70,7 @@ impl Rosenpass {
}
/// Write a config to a file
pub fn store<P: AsRef<Path>>(&self, p: P) -> anyhow::Result<()> {
pub fn store<P: AsRef<Path>>(&self, p: P) -> Result<()> {
let serialized_config =
toml::to_string_pretty(&self).expect("unable to serialize the default config");
fs::write(p, serialized_config)?;
@@ -78,7 +78,7 @@ impl Rosenpass {
}
/// Commit the configuration to where it came from, overwriting the original file
pub fn commit(&self) -> anyhow::Result<()> {
pub fn commit(&self) -> Result<()> {
let mut f = fopen_w(&self.config_file_path)?;
f.write_all(toml::to_string_pretty(&self)?.as_bytes())?;
@@ -86,36 +86,40 @@ impl Rosenpass {
}
/// Validate a configuration
pub fn validate(&self) -> anyhow::Result<()> {
pub fn validate(&self) -> Result<()> {
// check the public-key file exists
ensure!(
self.public_key.is_file(),
"public-key file {:?} does not exist",
self.public_key
);
if !(self.public_key.is_file()) {
return Err(RosenpassError::ConfigError(format!(
"public-key file {:?} does not exist",
self.public_key
)));
}
// check the secret-key file exists
ensure!(
self.secret_key.is_file(),
"secret-key file {:?} does not exist",
self.secret_key
);
if !(self.secret_key.is_file()) {
return Err(RosenpassError::ConfigError(format!(
"secret-key file {:?} does not exist",
self.secret_key
)));
}
for (i, peer) in self.peers.iter().enumerate() {
// check peer's public-key file exists
ensure!(
peer.public_key.is_file(),
"peer {i} public-key file {:?} does not exist",
peer.public_key
);
if !(peer.public_key.is_file()) {
return Err(RosenpassError::ConfigError(format!(
"peer {i} public-key file {:?} does not exist",
peer.public_key
)));
}
// check endpoint is usable
if let Some(addr) = peer.endpoint.as_ref() {
ensure!(
addr.to_socket_addrs().is_ok(),
"peer {i} endpoint {} can not be parsed to a socket address",
addr
);
if !(addr.to_socket_addrs().is_ok()) {
return Err(RosenpassError::ConfigError(format!(
"peer {i} endpoint {} can not be parsed to a socket address",
addr
)));
}
}
// TODO warn if neither out_key nor exchange_command is defined
@@ -151,7 +155,7 @@ impl Rosenpass {
/// from chaotic args
/// Quest: the grammar is undecideable, what do we do here?
pub fn parse_args(args: Vec<String>) -> anyhow::Result<Self> {
pub fn parse_args(args: Vec<String>) -> Result<Self> {
let mut config = Self::new("", "");
#[derive(Debug, Hash, PartialEq, Eq)]
@@ -174,6 +178,7 @@ impl Rosenpass {
// TODO idea: use config.peers.len() to give index of peer with conflicting argument
use State::*;
let mut problem = false;
let mut state = Own;
let mut current_peer = None;
let p_exists = "a peer should exist by now";
@@ -183,9 +188,7 @@ impl Rosenpass {
(Own, "public-key", None) => OwnPublicKey,
(Own, "secret-key", None) => OwnSecretKey,
(Own, "private-key", None) => {
log::warn!(
"the private-key argument is deprecated, please use secret-key instead"
);
warn!("the private-key argument is deprecated, please use secret-key instead");
OwnSecretKey
}
(Own, "listen", None) => OwnListen,
@@ -194,14 +197,16 @@ impl Rosenpass {
Own
}
(Own, "peer", None) => {
ensure!(
already_set.contains(&OwnPublicKey),
"public-key file must be set"
);
ensure!(
already_set.contains(&OwnSecretKey),
"secret-key file must be set"
);
if !(already_set.contains(&OwnPublicKey)) {
return Err(RosenpassError::ConfigError(
"public-key file must be set".into(),
));
}
if !(already_set.contains(&OwnSecretKey)) {
return Err(RosenpassError::ConfigError(
"secret-key file must be set".into(),
));
}
already_set.clear();
current_peer = Some(RosenpassPeer::default());
@@ -209,18 +214,20 @@ impl Rosenpass {
Peer
}
(OwnPublicKey, pk, None) => {
ensure!(
already_set.insert(OwnPublicKey),
"public-key was already set"
);
if !(already_set.insert(OwnPublicKey)) {
return Err(RosenpassError::ConfigError(
"public-key was already set".into(),
));
}
config.public_key = pk.into();
Own
}
(OwnSecretKey, sk, None) => {
ensure!(
already_set.insert(OwnSecretKey),
"secret-key was already set"
);
if !(already_set.insert(OwnSecretKey)) {
return Err(RosenpassError::ConfigError(
"secret-key was already set".into(),
));
}
config.secret_key = sk.into();
Own
}
@@ -248,36 +255,45 @@ impl Rosenpass {
(Peer, "outfile", Some(_)) => PeerOutfile,
(Peer, "wireguard", Some(_)) => PeerWireguardDev,
(PeerPublicKey, pk, Some(peer)) => {
ensure!(
already_set.insert(PeerPublicKey),
"public-key was already set"
);
if !(already_set.insert(PeerPublicKey)) {
return Err(RosenpassError::ConfigError(
"public-key was already set".into(),
));
}
peer.public_key = pk.into();
Peer
}
(PeerEndpoint, e, Some(peer)) => {
ensure!(already_set.insert(PeerEndpoint), "endpoint was already set");
if !already_set.insert(PeerEndpoint) {
error!("endpoint was already set");
problem = true;
}
peer.endpoint = Some(e.to_owned());
Peer
}
(PeerPsk, psk, Some(peer)) => {
ensure!(already_set.insert(PeerEndpoint), "peer psk was already set");
if !already_set.insert(PeerEndpoint) {
error!("peer psk was already set");
problem = true;
}
peer.pre_shared_key = Some(psk.into());
Peer
}
(PeerOutfile, of, Some(peer)) => {
ensure!(
already_set.insert(PeerOutfile),
"peer outfile was already set"
);
if !(already_set.insert(PeerOutfile)) {
return Err(RosenpassError::ConfigError(
"peer outfile was already set".into(),
));
}
peer.key_out = Some(of.into());
Peer
}
(PeerWireguardDev, dev, Some(peer)) => {
ensure!(
already_set.insert(PeerWireguardDev),
"peer wireguard-dev was already set"
);
if !(already_set.insert(PeerWireguardDev)) {
return Err(RosenpassError::ConfigError(
"peer wireguard-dev was already set".into(),
));
}
assert!(peer.wg.is_none());
peer.wg = Some(WireGuard {
device: dev.to_string(),
@@ -287,10 +303,11 @@ impl Rosenpass {
PeerWireguardPeer
}
(PeerWireguardPeer, p, Some(peer)) => {
ensure!(
already_set.insert(PeerWireguardPeer),
"peer wireguard-peer was already set"
);
if !(already_set.insert(PeerWireguardPeer)) {
return Err(RosenpassError::ConfigError(
"peer wireguard-peer was already set".into(),
));
}
peer.wg.as_mut().expect(wg_exists).peer = p.to_string();
PeerWireguardExtraArgs
}
@@ -305,14 +322,16 @@ impl Rosenpass {
// error cases
(Own, x, None) => {
bail!("unrecognised argument {x}");
error!("unrecognised argument {x}");
return Err(RosenpassError::RuntimeError);
}
(Own | OwnPublicKey | OwnSecretKey | OwnListen, _, Some(_)) => {
panic!("current_peer is not None while in Own* state, this must never happen")
}
(State::Peer, arg, Some(_)) => {
bail!("unrecongnised argument {arg}");
error!("unrecongnised argument {arg}");
return Err(RosenpassError::RuntimeError);
}
(
Peer
@@ -331,6 +350,10 @@ impl Rosenpass {
};
}
if problem {
return Err(RosenpassError::RuntimeError);
}
if let Some(p) = current_peer {
// TODO ensure peer is propagated with sufficient information
config.peers.push(p);

View File

@@ -2,8 +2,8 @@
//! ensures their uniqueness
use {
crate::Result,
crate::{prftree::PrfTree, sodium::KEY_SIZE},
anyhow::Result,
};
pub fn protocol() -> Result<PrfTree> {
@@ -21,12 +21,12 @@ macro_rules! prflabel {
}
}
prflabel!(protocol, mac, "mac");
prflabel!(protocol, cookie, "cookie");
prflabel!(protocol, peerid, "peer id");
prflabel!(protocol, mac, "mac");
prflabel!(protocol, cookie, "cookie");
prflabel!(protocol, peerid, "peer id");
prflabel!(protocol, biscuit_ad, "biscuit additional data");
prflabel!(protocol, ckinit, "chaining key init");
prflabel!(protocol, _ckextract, "chaining key extract");
prflabel!(protocol, ckinit, "chaining key init");
prflabel!(protocol, _ckextract, "chaining key extract");
macro_rules! prflabel_leaf {
($base:ident, $name:ident, $($lbl:expr),* ) => {
@@ -38,10 +38,10 @@ macro_rules! prflabel_leaf {
}
}
prflabel_leaf!(_ckextract, mix, "mix");
prflabel_leaf!(_ckextract, hs_enc, "handshake encryption");
prflabel_leaf!(_ckextract, ini_enc, "initiator handshake encryption");
prflabel_leaf!(_ckextract, res_enc, "responder handshake encryption");
prflabel_leaf!(_ckextract, mix, "mix");
prflabel_leaf!(_ckextract, hs_enc, "handshake encryption");
prflabel_leaf!(_ckextract, ini_enc, "initiator handshake encryption");
prflabel_leaf!(_ckextract, res_enc, "responder handshake encryption");
prflabel!(_ckextract, _user, "user");
prflabel!(_user, _rp, "rosenpass.eu");

View File

@@ -1,3 +1,5 @@
use protocol::{HandshakeStateMachine, PeerId, PeerPtr, SessionId};
#[macro_use]
pub mod util;
#[macro_use]
@@ -17,20 +19,89 @@ pub mod protocol;
pub enum RosenpassError {
#[error("error in OQS")]
Oqs,
#[error("error from external library while calling OQS")]
OqsExternalLib,
#[error("error while calling into libsodium")]
LibsodiumError(&'static str),
#[error("buffer size mismatch, required {required_size} but only found {actual_size}")]
BufferSizeMismatch {
required_size: usize,
actual_size: usize,
},
#[error("invalid message type")]
InvalidMessageType(u8),
#[error("peer id {0:?} already taken")]
PeerIdAlreadyTaken(PeerId),
#[error("session id {0:?} already taken")]
SessionIdAlreadyTaken(SessionId),
#[error("{0}")]
NotImplemented(&'static str),
#[error("{0}")]
ConfigError(String),
#[error("see last log messages")]
RuntimeError,
#[error("{0}")]
IoError(#[from] std::io::Error),
#[error("{0}")]
TomlDeserError(#[from] toml::de::Error),
#[error("{0}")]
TomlSerError(#[from] toml::ser::Error),
#[error("invalid session id {0:?} was used")]
InvalidSessionId(SessionId),
#[error("no session available")]
NoSession,
#[error("the peer {0:?} does not exist")]
NoSuchPeer(PeerPtr),
#[error("the peer id {0:?} does not exist")]
NoSuchPeerId(PeerId),
#[error("the session {0:?} does not exist")]
NoSuchSessionId(SessionId),
#[error("no current handshake with peer {0:?}")]
NoCurrentHs(PeerPtr),
// TODO implement Display for Peer/Session ptr?
#[error("message seal broken")]
SealBroken,
#[error("received empty message")]
EmptyMessage,
#[error("biscuit with invalid number")]
InvalidBiscuitNo,
#[error("got unexpected message")]
UnexpectedMessage {
session: SessionId,
expected: Option<HandshakeStateMachine>,
got: Option<HandshakeStateMachine>,
},
#[error("???")]
StaleNonce,
}
/// Rosenpass Result type
pub type Result<T> = core::result::Result<T, RosenpassError>;
impl RosenpassError {
/// Helper function to check a buffer size
fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<(), Self> {
fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<()> {
if required_size != actual_size {
Err(Self::BufferSizeMismatch {
required_size,
@@ -45,11 +116,11 @@ impl RosenpassError {
/// Extension trait to attach function calls to foreign types.
trait RosenpassMaybeError {
/// Checks whether something is an error or not
fn to_rg_error(&self) -> Result<(), RosenpassError>;
fn to_rg_error(&self) -> Result<()>;
}
impl RosenpassMaybeError for oqs_sys::common::OQS_STATUS {
fn to_rg_error(&self) -> Result<(), RosenpassError> {
fn to_rg_error(&self) -> Result<()> {
use oqs_sys::common::OQS_STATUS;
match self {
OQS_STATUS::OQS_SUCCESS => Ok(()),

View File

@@ -1,10 +1,8 @@
//! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf)
use {
crate::{
coloring::Secret,
sodium::{hmac, hmac_into, KEY_SIZE},
},
anyhow::Result,
use crate::{
coloring::Secret,
sodium::{hmac, hmac_into, KEY_SIZE},
Result,
};
// TODO Use a proper Dec interface

View File

@@ -66,6 +66,8 @@
//! # }
//! ```
use log::{trace, warn};
use crate::{
coloring::*,
labeled_prf as lprf,
@@ -74,8 +76,8 @@ use crate::{
prftree::{SecretPrfTree, SecretPrfTreeBranch},
sodium::*,
util::*,
Result, RosenpassError,
};
use anyhow::{bail, ensure, Context, Result};
use std::collections::hash_map::{
Entry::{Occupied, Vacant},
HashMap,
@@ -485,10 +487,8 @@ impl CryptoServer {
let peerid = peer.pidt()?;
let peerno = self.peers.len();
match self.index.entry(IndexKey::Peer(peerid)) {
Occupied(_) => bail!(
"Cannot insert peer with id {:?}; peer with this id already registered.",
peerid
),
// TODO improve type handling, use PeerPtr
Occupied(_) => return Err(RosenpassError::PeerIdAlreadyTaken(peerid)),
Vacant(e) => e.insert(peerno),
};
self.peers.push(peer);
@@ -500,7 +500,7 @@ impl CryptoServer {
pub fn register_session(&mut self, id: SessionId, peer: PeerPtr) -> Result<()> {
match self.index.entry(IndexKey::Sid(id)) {
Occupied(p) if PeerPtr(*p.get()) == peer => {} // Already registered
Occupied(_) => bail!("Cannot insert session with id {:?}; id is in use.", id),
Occupied(_) => return Err(RosenpassError::SessionIdAlreadyTaken(id)),
Vacant(e) => {
e.insert(peer.0);
}
@@ -522,8 +522,11 @@ impl CryptoServer {
};
}
pub fn find_peer(&self, id: PeerId) -> Option<PeerPtr> {
self.index.get(&IndexKey::Peer(id)).map(|no| PeerPtr(*no))
pub fn find_peer(&self, id: PeerId) -> Result<PeerPtr> {
self.index
.get(&IndexKey::Peer(id))
.map(|no| PeerPtr(*no))
.ok_or_else(|| RosenpassError::NoSuchPeerId(id))
}
// lookup_session in whitepaper
@@ -781,17 +784,21 @@ impl CryptoServer {
/// | t2 | `InitConf` | -> | |
/// | t3 | | <- | `EmptyData` |
pub fn handle_msg(&mut self, rx_buf: &[u8], tx_buf: &mut [u8]) -> Result<HandleMsgResult> {
let seal_broken = "Message seal broken!";
// length of the response. We assume no response, so None for now
let mut len = 0;
let mut exchanged = false;
ensure!(!rx_buf.is_empty(), "received empty message, ignoring it");
if rx_buf.is_empty() {
trace!("received empty message, ignoring it");
return Err(RosenpassError::EmptyMessage);
}
let peer = match rx_buf[0].try_into() {
Ok(MsgType::InitHello) => {
let msg_in = rx_buf.envelope::<InitHello<&[u8]>>()?;
ensure!(msg_in.check_seal(self)?, seal_broken);
if msg_in.check_seal(self)? {
return Err(RosenpassError::SealBroken);
}
let mut msg_out = tx_buf.envelope::<RespHello<&mut [u8]>>()?;
let peer = self.handle_init_hello(
@@ -803,7 +810,9 @@ impl CryptoServer {
}
Ok(MsgType::RespHello) => {
let msg_in = rx_buf.envelope::<RespHello<&[u8]>>()?;
ensure!(msg_in.check_seal(self)?, seal_broken);
if msg_in.check_seal(self)? {
return Err(RosenpassError::SealBroken);
}
let mut msg_out = tx_buf.envelope::<InitConf<&mut [u8]>>()?;
let peer = self.handle_resp_hello(
@@ -818,7 +827,9 @@ impl CryptoServer {
}
Ok(MsgType::InitConf) => {
let msg_in = rx_buf.envelope::<InitConf<&[u8]>>()?;
ensure!(msg_in.check_seal(self)?, seal_broken);
if msg_in.check_seal(self)? {
return Err(RosenpassError::SealBroken);
}
let mut msg_out = tx_buf.envelope::<EmptyData<&mut [u8]>>()?;
let peer = self.handle_init_conf(
@@ -831,15 +842,23 @@ impl CryptoServer {
}
Ok(MsgType::EmptyData) => {
let msg_in = rx_buf.envelope::<EmptyData<&[u8]>>()?;
ensure!(msg_in.check_seal(self)?, seal_broken);
if msg_in.check_seal(self)? {
return Err(RosenpassError::SealBroken);
}
self.handle_resp_conf(msg_in.payload().empty_data()?)?
}
Ok(MsgType::DataMsg) => bail!("DataMsg handling not implemented!"),
Ok(MsgType::CookieReply) => bail!("CookieReply handling not implemented!"),
Err(_) => {
bail!("CookieReply handling not implemented!")
Ok(MsgType::DataMsg) => {
return Err(RosenpassError::NotImplemented(
"DataMsg handling not implemented!",
))
}
Ok(MsgType::CookieReply) => {
return Err(RosenpassError::NotImplemented(
"CookieReply handling not implemented!",
))
}
Err(e) => return Err(e),
};
Ok(HandleMsgResult {
@@ -1118,10 +1137,10 @@ impl CryptoServer {
impl IniHsPtr {
pub fn store_msg_for_retransmission(&self, srv: &mut CryptoServer, msg: &[u8]) -> Result<()> {
let ih = self
.get_mut(srv)
.as_mut()
.with_context(|| format!("No current handshake for peer {:?}", self.peer()))?;
let ih = self.get_mut(srv).as_mut().ok_or_else(|| {
warn!("No current handshake for peer {:?}", self.peer());
RosenpassError::NoCurrentHs(self.peer())
})?;
cpy_min(msg, &mut *ih.tx_buf);
ih.tx_count = 0;
ih.tx_len = msg.len();
@@ -1130,20 +1149,20 @@ impl IniHsPtr {
}
pub fn apply_retransmission(&self, srv: &mut CryptoServer, tx_buf: &mut [u8]) -> Result<usize> {
let ih = self
.get_mut(srv)
.as_mut()
.with_context(|| format!("No current handshake for peer {:?}", self.peer()))?;
let ih = self.get_mut(srv).as_mut().ok_or_else(|| {
warn!("No current handshake for peer {:?}", self.peer());
RosenpassError::NoCurrentHs(self.peer())
})?;
cpy_min(&ih.tx_buf[..ih.tx_len], tx_buf);
Ok(ih.tx_len)
}
pub fn register_retransmission(&self, srv: &mut CryptoServer) -> Result<()> {
let tb = srv.timebase.clone();
let ih = self
.get_mut(srv)
.as_mut()
.with_context(|| format!("No current handshake for peer {:?}", self.peer()))?;
let ih = self.get_mut(srv).as_mut().ok_or_else(|| {
warn!("No current handshake for peer {:?}", self.peer());
RosenpassError::NoCurrentHs(self.peer())
})?;
// Base delay, exponential increase, ±50% jitter
ih.tx_retry_at = tb.now()
+ RETRANSMIT_DELAY_BEGIN
@@ -1347,18 +1366,16 @@ impl HandshakeState {
hs.mix(biscuit_ct)?;
// Look up the associated peer
let peer = srv
.find_peer(pid) // TODO: FindPeer should return a Result<()>
.with_context(|| format!("Could not decode biscuit for peer {pid:?}: No such peer."))?;
let peer = srv.find_peer(pid)?;
// Defense against replay attacks; implementations may accept
// the most recent biscuit no again (bn = peer.bn_{prev}) which
// indicates retransmission
// TODO: Handle retransmissions without involving the crypto code
ensure!(
sodium_bigint_cmp(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0,
"Rejecting biscuit: Outdated biscuit number"
);
if sodium_bigint_cmp(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0 {
warn!("Rejecting biscuit: Outdated biscuit number");
return Err(RosenpassError::InvalidBiscuitNo);
}
Ok((peer, no, hs))
}
@@ -1392,11 +1409,10 @@ impl CryptoServer {
///
/// Fail if no session is available with the peer
pub fn osk(&self, peer: PeerPtr) -> Result<SymKey> {
let session = peer
.session()
.get(self)
.as_ref()
.with_context(|| format!("No current session for peer {:?}", peer))?;
let session = peer.session().get(self).as_ref().ok_or_else(|| {
warn!("No current session for peer {peer:?}");
RosenpassError::NoSuchPeer(peer)
})?;
Ok(session.ck.mix(&lprf::osk()?)?.into_secret())
}
}
@@ -1476,8 +1492,7 @@ impl CryptoServer {
let peer = {
let mut peerid = PeerId::zero();
core.decrypt_and_mix(&mut *peerid, ih.pidic())?;
self.find_peer(peerid)
.with_context(|| format!("No such peer {peerid:?}."))?
self.find_peer(peerid)?
};
// IHR7
@@ -1519,13 +1534,12 @@ impl CryptoServer {
mut ic: InitConf<&mut [u8]>,
) -> Result<PeerPtr> {
// RHI2
let sid = SessionId::from_slice(rh.sidi());
let peer = self
.lookup_handshake(SessionId::from_slice(rh.sidi()))
.with_context(|| {
format!(
"Got RespHello packet for non-existent session {:?}",
rh.sidi()
)
.lookup_handshake(sid)
.ok_or_else(|| {
warn!("Got RespHello packet for non-existent session {sid:?}");
RosenpassError::NoSuchSessionId(sid)
})?
.peer();
@@ -1546,13 +1560,14 @@ impl CryptoServer {
let exp = hs!().next;
let got = HandshakeStateMachine::RespHello;
ensure!(
exp == got,
"Unexpected package in session {:?}. Expected {:?}, got {:?}.",
SessionId::from_slice(rh.sidi()),
exp,
got
);
if exp != got {
warn!("Unexpected package in session {sid:?}. Expected {exp:?}, got {got:?}.",);
return Err(RosenpassError::UnexpectedMessage {
session: sid,
expected: Some(exp),
got: Some(got),
});
}
let mut core = hs!().core.clone();
core.sidr.copy_from_slice(rh.sidr());
@@ -1669,11 +1684,10 @@ impl CryptoServer {
// instead of a generic PeerPtr::send(&Server, Option<&[u8]>) -> Either<EmptyData, Data>
// because data transmission is a stub currently. This software is supposed to be used
// as a key exchange service feeding a PSK into some classical (i.e. non post quantum)
let ses = peer
.session()
.get_mut(self)
.as_mut()
.context("Cannot send acknowledgement. No session.")?;
let ses = peer.session().get_mut(self).as_mut().ok_or_else(|| {
warn!("Cannot send acknowledgement. No session.");
RosenpassError::NoSession
})?;
rc.sid_mut().copy_from_slice(&ses.sidt.value);
rc.ctr_mut().copy_from_slice(&ses.txnm.to_le_bytes());
ses.txnm += 1; // Increment nonce before encryption, just in case an error is raised
@@ -1687,30 +1701,39 @@ impl CryptoServer {
pub fn handle_resp_conf(&mut self, rc: EmptyData<&[u8]>) -> Result<PeerPtr> {
let sid = SessionId::from_slice(rc.sid());
let hs = self
.lookup_handshake(sid)
.with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?;
let hs = self.lookup_handshake(sid).ok_or_else(|| {
warn!("Got RespConf packet for non-existent session {sid:?}");
RosenpassError::InvalidSessionId(sid)
})?;
let ses = hs.peer().session();
let exp = hs.get(self).as_ref().map(|h| h.next);
let got = Some(HandshakeStateMachine::RespConf);
ensure!(
exp == got,
"Unexpected package in session {:?}. Expected {:?}, got {:?}.",
sid,
exp,
got
);
if exp != got {
warn!("Unexpected package in session {sid:?}. Expected {exp:?}, got {got:?}.",);
return Err(RosenpassError::UnexpectedMessage {
session: sid,
expected: exp,
got,
});
}
// Validate the message
{
let s = ses.get_mut(self).as_mut().with_context(|| {
format!("Cannot validate EmptyData message. Missing encryption session for {sid:?}")
// TODO get rid of as_mut necessity
let s = ses.get_mut(self).as_mut().ok_or_else(|| {
warn!("Cannot validate EmptyData message. Missing encryption session for {sid:?}");
RosenpassError::NoSuchSessionId(sid)
})?;
// the unwrap can not fail, because the slice returned by ctr() is
// guaranteed to have the correct size
let n = u64::from_le_bytes(rc.ctr().try_into().unwrap());
ensure!(n >= s.txnt, "Stale nonce");
if n < s.txnt {
trace!("Stale nonce");
return Err(RosenpassError::StaleNonce);
}
s.txnt = n;
aead_dec_into(
// pt, k, n, ad, ct

View File

@@ -1,7 +1,6 @@
//! Bindings and helpers for accessing libsodium functions
use crate::util::*;
use anyhow::{ensure, Result};
use crate::{util::*, Result, RosenpassError};
use libsodium_sys as libsodium;
use log::trace;
use static_assertions::const_assert_eq;
@@ -26,9 +25,11 @@ const_assert_eq!(KEY_SIZE, libsodium::crypto_generichash_BYTES as usize);
macro_rules! sodium_call {
($name:ident, $($args:expr),*) => { attempt!({
ensure!(unsafe{libsodium::$name($($args),*)} > -1,
"Error in libsodium's {}.", stringify!($name));
Ok(())
if unsafe{libsodium::$name($($args),*)} > -1 {
Ok(())
}else{
Err(RosenpassError::LibsodiumError(concat!("Error in libsodium's {}.", stringify!($name))))
}
})};
($name:ident) => { sodium_call!($name, ) };
}
@@ -251,7 +252,12 @@ pub fn mac16(key: &[u8], data: &[u8]) -> Result<[u8; 16]> {
pub fn hmac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_SIZE);
if key.len() != KEY_SIZE {
return Err(crate::RosenpassError::BufferSizeMismatch {
required_size: KEY_SIZE,
actual_size: key.len(),
});
}
const IPAD: [u8; KEY_SIZE] = [0x36u8; KEY_SIZE];
let mut temp_key = [0u8; KEY_SIZE];

View File

@@ -1,9 +1,9 @@
//! Helper functions and macros
use anyhow::{ensure, Context, Result};
use base64::{
display::Base64Display as B64Display, read::DecoderReader as B64Reader,
write::EncoderWriter as B64Writer,
};
use log::error;
use std::{
borrow::{Borrow, BorrowMut},
cmp::min,
@@ -13,7 +13,10 @@ use std::{
time::{Duration, Instant},
};
use crate::coloring::{Public, Secret};
use crate::{
coloring::{Public, Secret},
Result,
};
#[inline]
pub fn xor_into(a: &mut [u8], b: &[u8]) {
@@ -58,7 +61,7 @@ pub fn cpy_min<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, d
#[macro_export]
macro_rules! attempt {
($block:expr) => {
(|| -> ::anyhow::Result<_> { $block })()
(|| -> crate::Result<_> { $block })()
};
}
@@ -150,8 +153,12 @@ impl<R: Read> ReadExactToEnd for R {
fn read_exact_to_end(&mut self, buf: &mut [u8]) -> Result<()> {
let mut dummy = [0u8; 8];
self.read_exact(buf)?;
ensure!(self.read(&mut dummy)? == 0, "File too long!");
Ok(())
if self.read(&mut dummy)? != 0 {
error!("File too long!");
Err(crate::RosenpassError::RuntimeError)
} else {
Ok(())
}
}
}
@@ -185,9 +192,10 @@ impl<const N: usize> LoadValue for Secret<N> {
fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
let mut v = Self::random();
let p = path.as_ref();
fopen_r(p)?
.read_exact_to_end(v.secret_mut())
.with_context(|| format!("Could not load file {p:?}"))?;
fopen_r(p)?.read_exact_to_end(v.secret_mut()).map_err(|e| {
error!("Could not load file {p:?}");
e
})?;
Ok(v)
}
}
@@ -205,7 +213,10 @@ impl<const N: usize> LoadValueB64 for Secret<N> {
// not worth it right now.
b64_reader(&mut fopen_r(p)?)
.read_exact(v.secret_mut())
.with_context(|| format!("Could not load base64 file {p:?}"))?;
.map_err(|e| {
error!("Could not load base64 file {p:?}");
e
})?;
Ok(v)
}
}