Compare commits

...

2 Commits

Author SHA1 Message Date
wucke13
63ff75b93c 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.
2023-06-17 22:43:45 +02:00
wucke13
5bebdd9284 fix broken devShell
The use of a fakecmake in the main step of the Rosenpass build removed real CMake from the devShell, essentially breaking cargo build from within it. This commit fixes that, by explicitly placing the real CMake in the devShell's nativeBuildInputs.
2023-06-17 22:41:56 +02:00
12 changed files with 368 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

@@ -312,6 +312,7 @@
inherit (packages.rosenpass) RUST_MIN_STACK;
inputsFrom = [ packages.default ];
nativeBuildInputs = with pkgs; [
cmake # override the fakecmake from the main step above
cargo-release
clippy
nodePackages.prettier

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)
}
}