[−]Struct ossuary::OssuaryConnection
Context for interacting with an encrypted communication channel
All interaction with ossuary's encrypted channels is performed via a OssuaryConnection instance. It holds all of the state required to maintain one side of an encrypted connection.
A context is created with OssuaryConnection::new
, passing it a
ConnectionType
identifying whether it is to act as a client or server.
Server contexts can optionally require authentication, verified by providing
a list of public keys of permitted clients with
OssuaryConnection::add_authorized_keys
. Clients, on the other hand,
authenticate by setting their secret key with
OssuaryConnection::set_secret_key
.
A server must create one OssuaryConnection per connected client. Multiple connections cannot be multiplexed in one context.
A OssuaryConnection keeps temporary buffers for both received and soon-to-be transmitted data. This means they are not particularly small objects, but in exchange they can read and write from/to streams set in non-blocking mode without blocking single-threaded applications.
Establishing a connection involves calling
OssuaryConnection::send_handshake
and
OssuaryConnection::recv_handshake
in a loop until
OssuaryConnection::handshake_done
returns true.
Once established, data to encrypt and send is passed to
OssuaryConnection::send_data
and received data to decrypt is
passed to OssuaryConnection::recv_data
.
Your program should be structured so that any failures during transmission cause it to fall back to the handshake loop to attempt a reconnection.
Methods
impl OssuaryConnection
pub fn new(
conn_type: ConnectionType,
auth_secret_key: Option<&[u8]>
) -> Result<OssuaryConnection, OssuaryError>
conn_type: ConnectionType,
auth_secret_key: Option<&[u8]>
) -> Result<OssuaryConnection, OssuaryError>
Allocate a new OssuaryConnection.
conn_type
is a ConnectionType
indicating whether this instance
is for a client or server.
auth_secret_key
is the secret portion of the long-term Ed25519 key
used for host authentication. If None
is provided, a keypair will
be generated for the lifetime of this connection object. This key
can be changed with OssuaryConnection::set_secret_key
.
pub fn disconnect(&mut self, error: bool)
Terminate a connection, or an on-going connection attempt.
Calling this immediately closes the local end of Ossuary's connection, and queues a disconnect packet to be sent to the remote host to inform it to close its end.
After calling disconnect(), the application should continue calling
Ossuary's functions (or at least its handshake functions) in a loop
until OssuaryConnection::handshake_done
returns the matching error.
This allows Ossuary to generate the final disconnect packet.
The handshake will return OssuaryError::ConnectionFailed
if 'error'
is true, or OssuaryError::ConnectionClosed
otherwise.
'error' - Indicates the reason for termination. True means the channel is being closed because of some error, False means it is being closed due to completion or a clean shutdown.
pub fn is_server(&self) -> bool
Whether this context represents a server (as opposed to a client).
pub fn add_authorized_key(&mut self, key: &[u8]) -> Result<(), OssuaryError>
Add public key of permitted remote hosts
During the handshake, both hosts will be required to sign a challenge with their secret authentication key. The host sends both the signature and the public key it signed with. The other side validates the signature, and verifies that the public key is in the list of authorized keys.
Unauthenticated servers do not verify the public key. Authenticated
servers do verify the public key, and reject the connection if the key
is unknown. Clients verify the public key, and raise
OssuaryError::UntrustedServer
if the key is unknown, permitting a
Trust-On-First-Use scheme if desired.
If a key is rejected, permanent connection failures are raised on both sides.
pub fn add_authorized_keys<'a, T>(
&mut self,
keys: T
) -> Result<usize, OssuaryError> where
T: IntoIterator<Item = &'a [u8]>,
&mut self,
keys: T
) -> Result<usize, OssuaryError> where
T: IntoIterator<Item = &'a [u8]>,
Add public keys of permitted remote hosts
keys
must be an iterable of &[u8]
slices containing valid 32-byte
ed25519 public keys.
See OssuaryConnection::add_authorized_key
for documentation.
pub fn set_secret_key(&mut self, key: &[u8]) -> Result<(), OssuaryError>
Set authentication secret signing key
Changes the secret authentication key of this side of the connection,
which was previously set by OssuaryConnection::new
key
must be a &[u8]
slice containing a valid 32-byte ed25519
signing key. Signing keys should be kept secret and should be stored
securely.
This key is used to authenticate during the handshake if the remote server requires authentication. During the handshake, the server will send a challenge (a buffer of random bytes) which the client signs with this secret key. The client returns its public key and the signature of the challenge data to identify which key it is using for authentication, and to prove possession of the secret key.
pub fn local_public_key(&self) -> Result<&[u8], OssuaryError>
Get the local host's authentication public key
When a secret key is set with OssuaryConnection::set_secret_key
, the
matching public key is calculated. This function returns that public
key, which can be shared with a remote server for future authentication.
pub fn remote_public_key(&self) -> Result<&[u8], OssuaryError>
Get the remote host's authentication public key
When a connection is established, or during the initial handshake after
reeiving an OssuaryError::UntrustedServer
response, this returns the
remote side's authentication public key. This is typically needed by a
client to get the remote server's key for a Trust-On-First-Use scheme.
impl OssuaryConnection
pub fn send_handshake<T, U>(&mut self, buf: T) -> Result<usize, OssuaryError> where
T: DerefMut<Target = U>,
U: Write,
T: DerefMut<Target = U>,
U: Write,
Write the next handshake packet into the given buffer
If a handshake packet is ready to be sent, this function writes the encrypted packet into the provided buffer.
This is a critical part of the handshaking stage, when a connection to
a remote host is securely established. Each side of the connection must
call send_handshake() continuously, and any data that is written to the
data buffer must be sent to the remote host. This should be done until
OssuaryConnection::handshake_done()
returns true.
Note that Ossuary does not perform network operations itself. It is the caller's responsibility to put the written data on the wire. However, you may pass a 'buf' that does this automatically, such as a TcpStream.
Returns the number of bytes written into buf
, or an error. You must
handle OssuaryError::WouldBlock
, which is a recoverable error, but
indicates that some bytes were written to the buffer.
pub fn recv_handshake<T, U>(&mut self, buf: T) -> Result<usize, OssuaryError> where
T: DerefMut<Target = U>,
U: Read,
T: DerefMut<Target = U>,
U: Read,
Read the next handshake packet from the given buffer
If a handshake packet has been received, this function reads and parses the encrypted packet from the provided buffer and updates its internal connection state.
This is a critical part of the handshaking stage, when a connection to
a remote host is securely established. Each side of the connection must
call recv_handshake() whenever data is received from the network until
OssuaryConnection::handshake_done()
returns true.
Returns the number of bytes read from buf
, or an error. It is the
caller's responsibility to ensure that the consumed bytes are removed
from the data buffer before it is used again. You must handle
OssuaryError::WouldBlock
, which is a recoverable error, but
indicates that some bytes were also read from the buffer.
pub fn handshake_done(&mut self) -> Result<bool, OssuaryError>
Returns whether the handshake process is complete.
Returns an error if the connection has failed, and specifically raises
OssuaryError::UntrustedServer
if the handshake has stalled because
the remote host sent an authentication key that is not trusted.
In the event of an untrusted server, calling
OssuaryConnection::add_authorized_key
will mark the key as trusted
and allow the handshake to continue. This should only be done if the
application is implementing a Trust-On-First-Use policy, and has
verified that the remote host's key has never been seen before. It is
always best practice to prompt the user in this case before continuing.
impl OssuaryConnection
pub fn send_data<T, U>(
&mut self,
in_buf: &[u8],
out_buf: T
) -> Result<usize, OssuaryError> where
T: DerefMut<Target = U>,
U: Write,
&mut self,
in_buf: &[u8],
out_buf: T
) -> Result<usize, OssuaryError> where
T: DerefMut<Target = U>,
U: Write,
Encrypts data into a packet suitable for sending over the network
The caller provides unencrypted plaintext data, in any format, in the
in_buf
buffer. send_data()
encrypts it and writes it in the proper
packet format into out_buf
.
This is the core function for data transmission via ossuary. All data to be sent over an Ossuary connection should pass through this function.
Note that Ossuary does not perform network operations itself. It is the caller's responsibility to put the written data on the wire. However, you may pass a 'buf' that does this automatically, such as a TcpStream.
Returns the number of bytes written to out_buf
, or an error.
You must handle OssuaryError::WouldBlock
, which is a recoverable
error, but indicates that some bytes were written to the buffer. If any
bytes are written to out_buf
, it can be assumed that all of in_buf
was consumed. In the event of a WouldBlock
error, you can either
continue calling send_data()
with the next data to be sent, or you can
use OssuaryConnection::flush()
to explicitly finish writing the
packet.
pub fn recv_data<T, U, R, V>(
&mut self,
in_buf: T,
out_buf: R
) -> Result<(usize, usize), OssuaryError> where
T: DerefMut<Target = U>,
U: Read,
R: DerefMut<Target = V>,
V: Write,
&mut self,
in_buf: T,
out_buf: R
) -> Result<(usize, usize), OssuaryError> where
T: DerefMut<Target = U>,
U: Read,
R: DerefMut<Target = V>,
V: Write,
Decrypts data from a packet received from a remote host
The caller provides encrypted data from a remote host in the in_buf
buffer. recv_data()
decrypts it and writes the plaintext result into
out_buf
.
This is the core function for data transmission via ossuary. All data received over an Ossuary connection should pass through this function.
Returns the number of bytes written to out_buf
, or an error.
You must handle OssuaryError::WouldBlock
, which is a recoverable
error, but indicates that some bytes were read from in_buf
. This
indicates that an incomplete packet was received.
pub fn flush<R, V>(&mut self, out_buf: R) -> Result<usize, OssuaryError> where
R: DerefMut<Target = V>,
V: Write,
R: DerefMut<Target = V>,
V: Write,
Write any cached encrypted data waiting to be sent
If a previous call to OssuaryConnection::send_data
was unable to
write out all of its data, the remaining data is cached internally. It
can be explicitly flushed by calling this function until it returns 0.
After each call, it is the caller's responsibility to put the written
data onto the network, unless out_buf
is an object that handles that
implicitly, such as a TcpStream.
Trait Implementations
impl Default for OssuaryConnection
fn default() -> Self
Auto Trait Implementations
impl Send for OssuaryConnection
impl Sync for OssuaryConnection
Blanket Implementations
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T> From<T> for T
[src]
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,
type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,
impl<T> Clear for T where
T: InitializableFromZeroed + ?Sized,
T: InitializableFromZeroed + ?Sized,
fn clear(&mut self)
impl<T> InitializableFromZeroed for T where
T: Default,
T: Default,
unsafe fn initialize(place: *mut T)
impl<T> Same<T> for T
type Output = T
Should always be Self