Better management of key material

This commit is contained in:
Mathias Hall-Andersen
2019-08-31 15:03:14 +02:00
parent 0520b28ac2
commit 51179f5433
7 changed files with 234 additions and 223 deletions

1
Cargo.lock generated
View File

@@ -1241,6 +1241,7 @@ dependencies = [
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chacha20poly1305 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "chacha20poly1305 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -25,6 +25,7 @@ hjul = "0.1.2"
ring = "0.16.7" ring = "0.16.7"
chacha20poly1305 = "^0.1" chacha20poly1305 = "^0.1"
aead = "^0.1.1" aead = "^0.1.1"
clear_on_drop = "0.2.3"
[dependencies.x25519-dalek] [dependencies.x25519-dalek]
version = "^0.5" version = "^0.5"

View File

@@ -15,6 +15,8 @@ use rand::{CryptoRng, RngCore};
use generic_array::typenum::*; use generic_array::typenum::*;
use generic_array::*; use generic_array::*;
use clear_on_drop::clear_stack_on_return;
use super::device::Device; use super::device::Device;
use super::messages::{NoiseInitiation, NoiseResponse}; use super::messages::{NoiseInitiation, NoiseResponse};
use super::messages::{TYPE_INITIATION, TYPE_RESPONSE}; use super::messages::{TYPE_INITIATION, TYPE_RESPONSE};
@@ -39,6 +41,9 @@ const SIZE_HS: usize = 32;
const SIZE_NONCE: usize = 8; const SIZE_NONCE: usize = 8;
const SIZE_TAG: usize = 16; const SIZE_TAG: usize = 16;
// number of pages to clear after sensitive call
const CLEAR_PAGES: usize = 1;
// C := Hash(Construction) // C := Hash(Construction)
const INITIAL_CK: [u8; SIZE_CK] = [ const INITIAL_CK: [u8; SIZE_CK] = [
0x60, 0xe2, 0x6d, 0xae, 0xf3, 0x27, 0xef, 0xc0, 0x2e, 0xc3, 0x35, 0xe2, 0xa0, 0x25, 0xd2, 0xd0, 0x60, 0xe2, 0x6d, 0xae, 0xf3, 0x27, 0xef, 0xc0, 0x2e, 0xc3, 0x35, 0xe2, 0xa0, 0x25, 0xd2, 0xd0,
@@ -103,21 +108,21 @@ macro_rules! KDF3 {
} }
macro_rules! SEAL { macro_rules! SEAL {
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {{ ($key:expr, $ad:expr, $pt:expr, $ct:expr) => {
let ct = ChaCha20Poly1305::new(*GenericArray::from_slice($key)) ChaCha20Poly1305::new(*GenericArray::from_slice($key))
.encrypt(&ZERO_NONCE.into(), Payload { msg: $pt, aad: $ad }) .encrypt(&ZERO_NONCE.into(), Payload { msg: $pt, aad: $ad })
.unwrap(); .map(|ct| $ct.copy_from_slice(&ct))
$ct.copy_from_slice(&ct); .unwrap()
}}; };
} }
macro_rules! OPEN { macro_rules! OPEN {
($key:expr, $ad:expr, $pt:expr, $ct:expr) => {{ ($key:expr, $ad:expr, $pt:expr, $ct:expr) => {
ChaCha20Poly1305::new(*GenericArray::from_slice($key)) ChaCha20Poly1305::new(*GenericArray::from_slice($key))
.decrypt(&ZERO_NONCE.into(), Payload { msg: $ct, aad: $ad }) .decrypt(&ZERO_NONCE.into(), Payload { msg: $ct, aad: $ad })
.map_err(|_| HandshakeError::DecryptionFailure) .map_err(|_| HandshakeError::DecryptionFailure)
.map(|pt| $pt.copy_from_slice(&pt)) .map(|pt| $pt.copy_from_slice(&pt))
}}; };
} }
#[cfg(test)] #[cfg(test)]
@@ -211,144 +216,151 @@ pub fn create_initiation<T: Copy, R: RngCore + CryptoRng>(
sender: u32, sender: u32,
msg: &mut NoiseInitiation, msg: &mut NoiseInitiation,
) -> Result<(), HandshakeError> { ) -> Result<(), HandshakeError> {
// initialize state clear_stack_on_return(CLEAR_PAGES, || {
// initialize state
let ck = INITIAL_CK; let ck = INITIAL_CK;
let hs = INITIAL_HS; let hs = INITIAL_HS;
let hs = HASH!(&hs, peer.pk.as_bytes()); let hs = HASH!(&hs, peer.pk.as_bytes());
msg.f_type.set(TYPE_INITIATION as u32); msg.f_type.set(TYPE_INITIATION as u32);
msg.f_sender.set(sender); msg.f_sender.set(sender);
// (E_priv, E_pub) := DH-Generate() // (E_priv, E_pub) := DH-Generate()
let eph_sk = StaticSecret::new(rng); let eph_sk = StaticSecret::new(rng);
let eph_pk = PublicKey::from(&eph_sk); let eph_pk = PublicKey::from(&eph_sk);
// C := Kdf(C, E_pub) // C := Kdf(C, E_pub)
let ck = KDF1!(&ck, eph_pk.as_bytes()); let ck = KDF1!(&ck, eph_pk.as_bytes());
// msg.ephemeral := E_pub // msg.ephemeral := E_pub
msg.f_ephemeral = *eph_pk.as_bytes(); msg.f_ephemeral = *eph_pk.as_bytes();
// H := HASH(H, msg.ephemeral) // H := HASH(H, msg.ephemeral)
let hs = HASH!(&hs, msg.f_ephemeral); let hs = HASH!(&hs, msg.f_ephemeral);
// (C, k) := Kdf2(C, DH(E_priv, S_pub)) // (C, k) := Kdf2(C, DH(E_priv, S_pub))
let (ck, key) = KDF2!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes()); let (ck, key) = KDF2!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
// msg.static := Aead(k, 0, S_pub, H) // msg.static := Aead(k, 0, S_pub, H)
SEAL!( SEAL!(
&key, &key,
&hs, // ad &hs, // ad
device.pk.as_bytes(), // pt device.pk.as_bytes(), // pt
&mut msg.f_static // ct || tag &mut msg.f_static // ct || tag
); );
// H := Hash(H || msg.static) // H := Hash(H || msg.static)
let hs = HASH!(&hs, &msg.f_static[..]); let hs = HASH!(&hs, &msg.f_static[..]);
// (C, k) := Kdf2(C, DH(S_priv, S_pub)) // (C, k) := Kdf2(C, DH(S_priv, S_pub))
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes()); let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
// msg.timestamp := Aead(k, 0, Timestamp(), H) // msg.timestamp := Aead(k, 0, Timestamp(), H)
SEAL!( SEAL!(
&key, &key,
&hs, // ad &hs, // ad
&timestamp::now(), // pt &timestamp::now(), // pt
&mut msg.f_timestamp // ct || tag &mut msg.f_timestamp // ct || tag
); );
// H := Hash(H || msg.timestamp) // H := Hash(H || msg.timestamp)
let hs = HASH!(&hs, &msg.f_timestamp); let hs = HASH!(&hs, &msg.f_timestamp);
// update state of peer // update state of peer
peer.set_state(State::InitiationSent { peer.set_state(State::InitiationSent {
hs, hs,
ck, ck,
eph_sk, eph_sk,
sender, sender,
}); });
Ok(()) Ok(())
})
} }
pub fn consume_initiation<'a, T: Copy>( pub fn consume_initiation<'a, T: Copy>(
device: &'a Device<T>, device: &'a Device<T>,
msg: &NoiseInitiation, msg: &NoiseInitiation,
) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> { ) -> Result<(&'a Peer<T>, TemporaryState), HandshakeError> {
// initialize state clear_stack_on_return(CLEAR_PAGES, || {
// initialize state
let ck = INITIAL_CK; let ck = INITIAL_CK;
let hs = INITIAL_HS; let hs = INITIAL_HS;
let hs = HASH!(&hs, device.pk.as_bytes()); let hs = HASH!(&hs, device.pk.as_bytes());
// C := Kdf(C, E_pub) // C := Kdf(C, E_pub)
let ck = KDF1!(&ck, &msg.f_ephemeral); let ck = KDF1!(&ck, &msg.f_ephemeral);
// H := HASH(H, msg.ephemeral) // H := HASH(H, msg.ephemeral)
let hs = HASH!(&hs, &msg.f_ephemeral); let hs = HASH!(&hs, &msg.f_ephemeral);
// (C, k) := Kdf2(C, DH(E_priv, S_pub)) // (C, k) := Kdf2(C, DH(E_priv, S_pub))
let eph_r_pk = PublicKey::from(msg.f_ephemeral); let eph_r_pk = PublicKey::from(msg.f_ephemeral);
let (ck, key) = KDF2!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes()); let (ck, key) = KDF2!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
// msg.static := Aead(k, 0, S_pub, H) // msg.static := Aead(k, 0, S_pub, H)
let mut pk = [0u8; 32]; let mut pk = [0u8; 32];
OPEN!( OPEN!(
&key, &key,
&hs, // ad &hs, // ad
&mut pk, // pt &mut pk, // pt
&msg.f_static // ct || tag &msg.f_static // ct || tag
)?; )?;
let peer = device.lookup_pk(&PublicKey::from(pk))?; let peer = device.lookup_pk(&PublicKey::from(pk))?;
// H := Hash(H || msg.static) // H := Hash(H || msg.static)
let hs = HASH!(&hs, &msg.f_static[..]); let hs = HASH!(&hs, &msg.f_static[..]);
// (C, k) := Kdf2(C, DH(S_priv, S_pub)) // (C, k) := Kdf2(C, DH(S_priv, S_pub))
let (ck, key) = KDF2!(&ck, peer.ss.as_bytes()); let (ck, key) = KDF2!(&ck, peer.ss.as_bytes());
// msg.timestamp := Aead(k, 0, Timestamp(), H) // msg.timestamp := Aead(k, 0, Timestamp(), H)
let mut ts = timestamp::ZERO; let mut ts = timestamp::ZERO;
OPEN!( OPEN!(
&key, &key,
&hs, // ad &hs, // ad
&mut ts, // pt &mut ts, // pt
&msg.f_timestamp // ct || tag &msg.f_timestamp // ct || tag
)?; )?;
// check and update timestamp // check and update timestamp
peer.check_replay_flood(device, &ts)?; peer.check_replay_flood(device, &ts)?;
// H := Hash(H || msg.timestamp) // H := Hash(H || msg.timestamp)
let hs = HASH!(&hs, &msg.f_timestamp); let hs = HASH!(&hs, &msg.f_timestamp);
// return state (to create response) // clear initiation state
*peer.state.lock() = State::Reset;
Ok((peer, (msg.f_sender.get(), eph_r_pk, hs, ck))) // return state (to create response)
Ok((peer, (msg.f_sender.get(), eph_r_pk, hs, ck)))
})
} }
pub fn create_response<T: Copy, R: RngCore + CryptoRng>( pub fn create_response<T: Copy, R: RngCore + CryptoRng>(
@@ -358,79 +370,77 @@ pub fn create_response<T: Copy, R: RngCore + CryptoRng>(
state: TemporaryState, // state from "consume_initiation" state: TemporaryState, // state from "consume_initiation"
msg: &mut NoiseResponse, // resulting response msg: &mut NoiseResponse, // resulting response
) -> Result<KeyPair, HandshakeError> { ) -> Result<KeyPair, HandshakeError> {
// unpack state clear_stack_on_return(CLEAR_PAGES, || {
// unpack state
let (receiver, eph_r_pk, hs, ck) = state; let (receiver, eph_r_pk, hs, ck) = state;
msg.f_type.set(TYPE_RESPONSE as u32); msg.f_type.set(TYPE_RESPONSE as u32);
msg.f_sender.set(sender); msg.f_sender.set(sender);
msg.f_receiver.set(receiver); msg.f_receiver.set(receiver);
// (E_priv, E_pub) := DH-Generate() // (E_priv, E_pub) := DH-Generate()
let eph_sk = StaticSecret::new(rng); let eph_sk = StaticSecret::new(rng);
let eph_pk = PublicKey::from(&eph_sk); let eph_pk = PublicKey::from(&eph_sk);
// C := Kdf1(C, E_pub) // C := Kdf1(C, E_pub)
let ck = KDF1!(&ck, eph_pk.as_bytes()); let ck = KDF1!(&ck, eph_pk.as_bytes());
// msg.ephemeral := E_pub // msg.ephemeral := E_pub
msg.f_ephemeral = *eph_pk.as_bytes(); msg.f_ephemeral = *eph_pk.as_bytes();
// H := Hash(H || msg.ephemeral) // H := Hash(H || msg.ephemeral)
let hs = HASH!(&hs, &msg.f_ephemeral); let hs = HASH!(&hs, &msg.f_ephemeral);
// C := Kdf1(C, DH(E_priv, E_pub)) // C := Kdf1(C, DH(E_priv, E_pub))
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes()); let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
// C := Kdf1(C, DH(E_priv, S_pub)) // C := Kdf1(C, DH(E_priv, S_pub))
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes()); let ck = KDF1!(&ck, eph_sk.diffie_hellman(&peer.pk).as_bytes());
// (C, tau, k) := Kdf3(C, Q) // (C, tau, k) := Kdf3(C, Q)
let (ck, tau, key) = KDF3!(&ck, &peer.psk); let (ck, tau, key) = KDF3!(&ck, &peer.psk);
// H := Hash(H || tau) // H := Hash(H || tau)
let hs = HASH!(&hs, tau); let hs = HASH!(&hs, tau);
// msg.empty := Aead(k, 0, [], H) // msg.empty := Aead(k, 0, [], H)
SEAL!( SEAL!(
&key, &key,
&hs, // ad &hs, // ad
&[], // pt &[], // pt
&mut msg.f_empty // \epsilon || tag &mut msg.f_empty // \epsilon || tag
); );
/* not strictly needed // Not strictly needed
* // H := Hash(H || msg.empty) // let hs = HASH!(&hs, &msg.f_empty_tag);
* let hs = HASH!(&hs, &msg.f_empty_tag);
*/
// derive key-pair // derive key-pair
// (verbose code, due to GenericArray -> [u8; 32] conversion) let (key_recv, key_send) = KDF2!(&ck, &[]);
let (key_recv, key_send) = KDF2!(&ck, &[]); // return unconfirmed key-pair
// return unconfirmed key-pair Ok(KeyPair {
birth: Instant::now(),
Ok(KeyPair { initiator: false,
birth: Instant::now(), send: Key {
initiator: false, id: sender,
send: Key { key: key_send.into(),
id: sender, },
key: key_send.into(), recv: Key {
}, id: receiver,
recv: Key { key: key_recv.into(),
id: receiver, },
key: key_recv.into(), })
},
}) })
} }
@@ -438,73 +448,78 @@ pub fn consume_response<T: Copy>(
device: &Device<T>, device: &Device<T>,
msg: &NoiseResponse, msg: &NoiseResponse,
) -> Result<Output<T>, HandshakeError> { ) -> Result<Output<T>, HandshakeError> {
// retrieve peer and associated state clear_stack_on_return(CLEAR_PAGES, || {
// retrieve peer and copy associated state
let peer = device.lookup_id(msg.f_receiver.get())?;
let (hs, ck, sender, eph_sk) = match *peer.state.lock() {
State::InitiationSent {
hs,
ck,
sender,
ref eph_sk,
} => Ok((hs, ck, sender, StaticSecret::from(eph_sk.to_bytes()))),
_ => Err(HandshakeError::InvalidState),
}?;
let peer = device.lookup_id(msg.f_receiver.get())?; // C := Kdf1(C, E_pub)
let (hs, ck, sender, eph_sk) = match peer.get_state() {
State::Reset => Err(HandshakeError::InvalidState),
State::InitiationSent {
hs,
ck,
sender,
eph_sk,
} => Ok((hs, ck, sender, eph_sk)),
}?;
// C := Kdf1(C, E_pub) let ck = KDF1!(&ck, &msg.f_ephemeral);
let ck = KDF1!(&ck, &msg.f_ephemeral); // H := Hash(H || msg.ephemeral)
// H := Hash(H || msg.ephemeral) let hs = HASH!(&hs, &msg.f_ephemeral);
let hs = HASH!(&hs, &msg.f_ephemeral); // C := Kdf1(C, DH(E_priv, E_pub))
// C := Kdf1(C, DH(E_priv, E_pub)) let eph_r_pk = PublicKey::from(msg.f_ephemeral);
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
let eph_r_pk = PublicKey::from(msg.f_ephemeral); // C := Kdf1(C, DH(E_priv, S_pub))
let ck = KDF1!(&ck, eph_sk.diffie_hellman(&eph_r_pk).as_bytes());
// C := Kdf1(C, DH(E_priv, S_pub)) let ck = KDF1!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes());
let ck = KDF1!(&ck, device.sk.diffie_hellman(&eph_r_pk).as_bytes()); // (C, tau, k) := Kdf3(C, Q)
// (C, tau, k) := Kdf3(C, Q) let (ck, tau, key) = KDF3!(&ck, &peer.psk);
let (ck, tau, key) = KDF3!(&ck, &peer.psk); // H := Hash(H || tau)
// H := Hash(H || tau) let hs = HASH!(&hs, tau);
let hs = HASH!(&hs, tau); // msg.empty := Aead(k, 0, [], H)
// msg.empty := Aead(k, 0, [], H) OPEN!(
&key,
&hs, // ad
&mut [], // pt
&msg.f_empty // \epsilon || tag
)?;
OPEN!( // derive key-pair
&key,
&hs, // ad
&mut [], // pt
&msg.f_empty // \epsilon || tag
)?;
// derive key-pair let (key_send, key_recv) = KDF2!(&ck, &[]);
let (key_send, key_recv) = KDF2!(&ck, &[]); // null the state
// return confirmed key-pair *peer.state.lock() = State::Reset;
Ok(( // return confirmed key-pair
Some(peer.identifier), // proves overship of the public key (e.g. for updating the endpoint)
None, // no response message Ok((
Some(KeyPair { Some(peer.identifier), // proves ownership of the public key (e.g. for updating the endpoint)
birth: Instant::now(), None, // no response message
initiator: true, Some(KeyPair {
send: Key { birth: Instant::now(),
id: sender, initiator: true,
key: key_send.into(), send: Key {
}, id: sender,
recv: Key { key: key_send.into(),
id: msg.f_sender.get(), },
key: key_recv.into(), recv: Key {
}, id: msg.f_sender.get(),
}), key: key_recv.into(),
)) },
}),
))
})
} }

View File

@@ -9,6 +9,8 @@ use x25519_dalek::PublicKey;
use x25519_dalek::SharedSecret; use x25519_dalek::SharedSecret;
use x25519_dalek::StaticSecret; use x25519_dalek::StaticSecret;
use clear_on_drop::clear::Clear;
use super::device::Device; use super::device::Device;
use super::macs; use super::macs;
use super::timestamp; use super::timestamp;
@@ -27,9 +29,9 @@ pub struct Peer<T> {
pub(crate) identifier: T, pub(crate) identifier: T,
// mutable state // mutable state
state: Mutex<State>, pub(crate) state: Mutex<State>,
timestamp: Mutex<Option<timestamp::TAI64N>>, pub(crate) timestamp: Mutex<Option<timestamp::TAI64N>>,
last_initiation_consumption: Mutex<Option<Instant>>, pub(crate) last_initiation_consumption: Mutex<Option<Instant>>,
// state related to DoS mitigation fields // state related to DoS mitigation fields
pub(crate) macs: Mutex<macs::Generator>, pub(crate) macs: Mutex<macs::Generator>,
@@ -50,21 +52,15 @@ pub enum State {
}, },
} }
impl Clone for State { impl Drop for State {
fn clone(&self) -> State { fn drop(&mut self) {
match self { match self {
State::Reset => State::Reset, State::InitiationSent{hs, ck, ..} => {
State::InitiationSent { // eph_sk already cleared by dalek-x25519
sender, hs.clear();
eph_sk, ck.clear();
hs,
ck,
} => State::InitiationSent {
sender: *sender,
eph_sk: StaticSecret::from(eph_sk.to_bytes()),
hs: *hs,
ck: *ck,
}, },
_ => ()
} }
} }
} }
@@ -90,13 +86,6 @@ where
} }
} }
/// Return the state of the peer
///
/// # Arguments
pub fn get_state(&self) -> State {
self.state.lock().clone()
}
/// Set the state of the peer unconditionally /// Set the state of the peer unconditionally
/// ///
/// # Arguments /// # Arguments

View File

@@ -1,8 +1,5 @@
use std::net::SocketAddr; use std::net::SocketAddr;
/* The generic implementation (not supporting "sticky-sockets"), pub trait Endpoint: Into<SocketAddr> + From<SocketAddr> {}
* is to simply use SocketAddr directly as the endpoint.
*/
pub trait Endpoint: Into<SocketAddr> {}
impl<T> Endpoint for T where T: Into<SocketAddr> {} impl<T> Endpoint for T where T: Into<SocketAddr> + From<SocketAddr> {}

View File

@@ -1,15 +1,23 @@
use clear_on_drop::clear::Clear;
use std::time::Instant; use std::time::Instant;
/* This file holds types passed between components. /* This file holds types passed between components.
* Whenever a type cannot be held local to a single module. * Whenever a type cannot be held local to a single module.
*/ */
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub struct Key { pub struct Key {
pub key: [u8; 32], pub key: [u8; 32],
pub id: u32, pub id: u32,
} }
// zero key on drop
impl Drop for Key {
fn drop(&mut self) {
self.key.clear()
}
}
#[cfg(test)] #[cfg(test)]
impl PartialEq for Key { impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
@@ -17,7 +25,7 @@ impl PartialEq for Key {
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub struct KeyPair { pub struct KeyPair {
pub birth: Instant, // when was the key-pair created pub birth: Instant, // when was the key-pair created
pub initiator: bool, // has the key-pair been confirmed? pub initiator: bool, // has the key-pair been confirmed?

View File

@@ -1,6 +1,6 @@
use std::error; use std::error;
pub trait Tun: Send + Sync { pub trait Tun: Send + Sync + 'static {
type Error: error::Error; type Error: error::Error;
/// Returns the MTU of the device /// Returns the MTU of the device
@@ -22,13 +22,13 @@ pub trait Tun: Send + Sync {
/// ///
/// # Arguments /// # Arguments
/// ///
/// - dst: Destination buffer (enough space for MTU bytes + header) /// - buf: Destination buffer (enough space for MTU bytes + header)
/// - offset: Offset for the beginning of the IP packet /// - offset: Offset for the beginning of the IP packet
/// ///
/// # Returns /// # Returns
/// ///
/// The size of the IP packet (ignoring the header) or an std::error::Error instance: /// The size of the IP packet (ignoring the header) or an std::error::Error instance:
fn read(&self, dst: &mut [u8], offset: usize) -> Result<usize, Self::Error>; fn read(&self, buf: &mut [u8], offset: usize) -> Result<usize, Self::Error>;
/// Writes an IP packet to the tunnel device /// Writes an IP packet to the tunnel device
/// ///