mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-08-01 08:10:21 -07:00
chore(doc): Documentation & tests for rosenpass::msgs
This commit is contained in:
@@ -15,15 +15,56 @@ use super::RosenpassError;
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
||||
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
||||
|
||||
/// TODO: I don't know what this is; remove!
|
||||
pub const MSG_SIZE_LEN: usize = 1;
|
||||
|
||||
/// Size of the field [Envelope::reserved]
|
||||
pub const RESERVED_LEN: usize = 3;
|
||||
|
||||
/// Size of the field [Envelope::mac]
|
||||
pub const MAC_SIZE: usize = 16;
|
||||
/// Size of the field [Envelope::cookie]
|
||||
pub const COOKIE_SIZE: usize = 16;
|
||||
/// Size of session id fields such as [InitHello::sidi]
|
||||
pub const SID_LEN: usize = 4;
|
||||
|
||||
/// Type of the mac field in [Envelope]
|
||||
pub type MsgEnvelopeMac = [u8; 16];
|
||||
/// Type of the cookie field in [Envelope]
|
||||
pub type MsgEnvelopeCookie = MsgEnvelopeMac;
|
||||
|
||||
/// Header and footer included in all our packages,
|
||||
/// including a type field.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::msgs::{Envelope, InitHello};
|
||||
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
|
||||
/// use memoffset::offset_of;
|
||||
///
|
||||
/// // Zero-initialization
|
||||
/// let mut ih = Envelope::<InitHello>::new_zeroed();
|
||||
///
|
||||
/// // Edit fields normally
|
||||
/// ih.mac[0] = 1;
|
||||
///
|
||||
/// // Edit as binary
|
||||
/// ih.as_bytes_mut()[offset_of!(Envelope<InitHello>, msg_type)] = 23;
|
||||
/// assert_eq!(ih.msg_type, 23);;
|
||||
///
|
||||
/// // Conversion to bytes
|
||||
/// let mut ih2 = ih.as_bytes().to_owned();
|
||||
///
|
||||
/// // Setting msg_type field, again
|
||||
/// ih2[0] = 42;
|
||||
///
|
||||
/// // Zerocopy parsing
|
||||
/// let ih3 = Ref::<&mut [u8], Envelope<InitHello>>::new(&mut ih2).unwrap();
|
||||
/// assert_ne!(ih.as_bytes(), ih3.as_bytes());
|
||||
/// assert_eq!(ih3.msg_type, 42);
|
||||
/// ```
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes, Clone)]
|
||||
pub struct Envelope<M: AsBytes + FromBytes> {
|
||||
@@ -40,6 +81,40 @@ pub struct Envelope<M: AsBytes + FromBytes> {
|
||||
pub cookie: MsgEnvelopeCookie,
|
||||
}
|
||||
|
||||
/// This is the first message sent by the initiator to the responder
|
||||
/// during the execution of the Rosenpass protocol.
|
||||
///
|
||||
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Check out the code of [crate::protocol::CryptoServer::handle_initiation] (generation on
|
||||
/// iniatiator side) and [crate::protocol::CryptoServer::handle_init_hello] (processing on
|
||||
/// responder side) to understand how this is used.
|
||||
///
|
||||
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::msgs::{Envelope, InitHello};
|
||||
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
|
||||
/// use memoffset::span_of;
|
||||
///
|
||||
/// // Zero initialization
|
||||
/// let mut ih = Envelope::<InitHello>::new_zeroed();
|
||||
///
|
||||
/// // Conversion to byte representation
|
||||
/// let ih = ih.as_bytes_mut();
|
||||
///
|
||||
/// // Set value on byte representation
|
||||
/// ih[span_of!(Envelope<InitHello>, payload)][span_of!(InitHello, sidi)]
|
||||
/// .copy_from_slice(&[1,2,3,4]);
|
||||
///
|
||||
/// // Conversion from bytes
|
||||
/// let ih = Ref::<&mut [u8], Envelope<InitHello>>::new(ih).unwrap();
|
||||
///
|
||||
/// // Check that write above on byte representation was effective
|
||||
/// assert_eq!(ih.payload.sidi, [1,2,3,4]);
|
||||
/// ```
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct InitHello {
|
||||
@@ -55,6 +130,40 @@ pub struct InitHello {
|
||||
pub auth: [u8; aead::TAG_LEN],
|
||||
}
|
||||
|
||||
/// This is the second message sent by the responder to the initiator
|
||||
/// during the execution of the Rosenpass protocol in response to [InitHello].
|
||||
///
|
||||
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Check out the code of [crate::protocol::CryptoServer::handle_init_hello] (generation on
|
||||
/// responder side) and [crate::protocol::CryptoServer::handle_resp_hello] (processing on
|
||||
/// initiator side) to understand how this is used.
|
||||
///
|
||||
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::msgs::{Envelope, RespHello};
|
||||
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
|
||||
/// use memoffset::span_of;
|
||||
///
|
||||
/// // Zero initialization
|
||||
/// let mut ih = Envelope::<RespHello>::new_zeroed();
|
||||
///
|
||||
/// // Conversion to byte representation
|
||||
/// let ih = ih.as_bytes_mut();
|
||||
///
|
||||
/// // Set value on byte representation
|
||||
/// ih[span_of!(Envelope<RespHello>, payload)][span_of!(RespHello, sidi)]
|
||||
/// .copy_from_slice(&[1,2,3,4]);
|
||||
///
|
||||
/// // Conversion from bytes
|
||||
/// let ih = Ref::<&mut [u8], Envelope<RespHello>>::new(ih).unwrap();
|
||||
///
|
||||
/// // Check that write above on byte representation was effective
|
||||
/// assert_eq!(ih.payload.sidi, [1,2,3,4]);
|
||||
/// ```
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct RespHello {
|
||||
@@ -72,6 +181,40 @@ pub struct RespHello {
|
||||
pub biscuit: [u8; BISCUIT_CT_LEN],
|
||||
}
|
||||
|
||||
/// This is the third message sent by the initiator to the responder
|
||||
/// during the execution of the Rosenpass protocol in response to [RespHello].
|
||||
///
|
||||
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Check out the code of [crate::protocol::CryptoServer::handle_resp_hello] (generation on
|
||||
/// initiator side) and [crate::protocol::CryptoServer::handle_init_conf] (processing on
|
||||
/// responder side) to understand how this is used.
|
||||
///
|
||||
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::msgs::{Envelope, InitConf};
|
||||
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
|
||||
/// use memoffset::span_of;
|
||||
///
|
||||
/// // Zero initialization
|
||||
/// let mut ih = Envelope::<InitConf>::new_zeroed();
|
||||
///
|
||||
/// // Conversion to byte representation
|
||||
/// let ih = ih.as_bytes_mut();
|
||||
///
|
||||
/// // Set value on byte representation
|
||||
/// ih[span_of!(Envelope<InitConf>, payload)][span_of!(InitConf, sidi)]
|
||||
/// .copy_from_slice(&[1,2,3,4]);
|
||||
///
|
||||
/// // Conversion from bytes
|
||||
/// let ih = Ref::<&mut [u8], Envelope<InitConf>>::new(ih).unwrap();
|
||||
///
|
||||
/// // Check that write above on byte representation was effective
|
||||
/// assert_eq!(ih.payload.sidi, [1,2,3,4]);
|
||||
/// ```
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
|
||||
pub struct InitConf {
|
||||
@@ -85,6 +228,51 @@ pub struct InitConf {
|
||||
pub auth: [u8; aead::TAG_LEN],
|
||||
}
|
||||
|
||||
/// This is the fourth message sent by the initiator to the responder
|
||||
/// during the execution of the Rosenpass protocol in response to [RespHello].
|
||||
///
|
||||
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
|
||||
///
|
||||
/// This message does not serve a cryptographic purpose; it just tells the initiator
|
||||
/// to stop package retransmission.
|
||||
///
|
||||
/// This message should really be called `RespConf`, but when we wrote the protocol,
|
||||
/// we initially designed the protocol we still though Rosenpass itself should do
|
||||
/// payload transmission at some point so `EmptyData` could have served as a more generic
|
||||
/// mechanism.
|
||||
///
|
||||
/// We might add payload transmission in the future again, but we will treat
|
||||
/// it as a protocol extension if we do.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Check out the code of [crate::protocol::CryptoServer::handle_init_conf] (generation on
|
||||
/// responder side) and [crate::protocol::CryptoServer::handle_resp_conf] (processing on
|
||||
/// initiator side) to understand how this is used.
|
||||
///
|
||||
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::msgs::{Envelope, EmptyData};
|
||||
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
|
||||
/// use memoffset::span_of;
|
||||
///
|
||||
/// // Zero initialization
|
||||
/// let mut ih = Envelope::<EmptyData>::new_zeroed();
|
||||
///
|
||||
/// // Conversion to byte representation
|
||||
/// let ih = ih.as_bytes_mut();
|
||||
///
|
||||
/// // Set value on byte representation
|
||||
/// ih[span_of!(Envelope<EmptyData>, payload)][span_of!(EmptyData, sid)]
|
||||
/// .copy_from_slice(&[1,2,3,4]);
|
||||
///
|
||||
/// // Conversion from bytes
|
||||
/// let ih = Ref::<&mut [u8], Envelope<EmptyData>>::new(ih).unwrap();
|
||||
///
|
||||
/// // Check that write above on byte representation was effective
|
||||
/// assert_eq!(ih.payload.sid, [1,2,3,4]);
|
||||
/// ```
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)]
|
||||
pub struct EmptyData {
|
||||
@@ -96,6 +284,22 @@ pub struct EmptyData {
|
||||
pub auth: [u8; aead::TAG_LEN],
|
||||
}
|
||||
|
||||
/// Cookie encrypted and sent to the initiator by the responder in [RespHello]
|
||||
/// and returned by the initiator in [InitConf].
|
||||
///
|
||||
/// The encryption key is randomly chosen by the responder and frequently regenerated.
|
||||
/// Using this biscuit value in the protocol allows us to make sure that the responder
|
||||
/// is mostly stateless until full initiator authentication is achieved, which is needed
|
||||
/// to prevent denial of service attacks. See the [whitepaper](https://rosenpass.eu/whitepaper.pdf)
|
||||
/// ([/papers/whitepaper.md] in this repository).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// To understand how the biscuit is used, it is best to read
|
||||
/// the code of [crate::protocol::HandshakeState::store_biscuit] and
|
||||
/// [crate::protocol::HandshakeState::load_biscuit]
|
||||
///
|
||||
/// [Envelope] and [InitHello] contain some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct Biscuit {
|
||||
@@ -107,12 +311,32 @@ pub struct Biscuit {
|
||||
pub ck: [u8; KEY_LEN],
|
||||
}
|
||||
|
||||
// TODO: Deprecate
|
||||
/// This should be removed; it is a remnant from when we still though
|
||||
/// Rosenpass itself should handle payload transmission.
|
||||
///
|
||||
/// We might add payload transmission in the future again, but we will treat
|
||||
/// it as a protocol extension if we do.
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct DataMsg {
|
||||
pub dummy: [u8; 4],
|
||||
}
|
||||
|
||||
/// Specialized message for use in the cookie mechanism.
|
||||
///
|
||||
/// See the [whitepaper](https://rosenpass.eu/whitepaper.pdf) ([/papers/whitepaper.md] in this repository) for details.
|
||||
///
|
||||
/// Generally used together with [CookieReply] which brings this up to the size
|
||||
/// of [InitHello] to avoid amplification Denial of Service attacks.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// To understand how the biscuit is used, it is best to read
|
||||
/// the code of [crate::protocol::CryptoServer::handle_cookie_reply] and
|
||||
/// [crate::protocol::CryptoServer::handle_msg_under_load].
|
||||
///
|
||||
/// [Envelope] and [InitHello] contain some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct CookieReplyInner {
|
||||
@@ -126,6 +350,20 @@ pub struct CookieReplyInner {
|
||||
pub cookie_encrypted: [u8; xaead::NONCE_LEN + COOKIE_SIZE + xaead::TAG_LEN],
|
||||
}
|
||||
|
||||
/// Specialized message for use in the cookie mechanism.
|
||||
///
|
||||
/// This just brings [CookieReplyInner] up to the size
|
||||
/// of [InitHello] to avoid amplification Denial of Service attacks.
|
||||
///
|
||||
/// See the [whitepaper](https://rosenpass.eu/whitepaper.pdf) ([/papers/whitepaper.md] in this repository) for details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// To understand how the biscuit is used, it is best to read
|
||||
/// the code of [crate::protocol::CryptoServer::handle_cookie_reply] and
|
||||
/// [crate::protocol::CryptoServer::handle_msg_under_load].
|
||||
///
|
||||
/// [Envelope] and [InitHello] contain some extra examples on how to use structures from the [::zerocopy] crate.
|
||||
#[repr(packed)]
|
||||
#[derive(AsBytes, FromBytes, FromZeroes)]
|
||||
pub struct CookieReply {
|
||||
@@ -135,6 +373,9 @@ pub struct CookieReply {
|
||||
|
||||
// Traits /////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Appears to be unused.
|
||||
///
|
||||
/// TODO: Remove
|
||||
pub trait WireMsg: std::fmt::Debug {
|
||||
const MSG_TYPE: MsgType;
|
||||
const MSG_TYPE_U8: u8 = Self::MSG_TYPE as u8;
|
||||
@@ -143,23 +384,59 @@ pub trait WireMsg: std::fmt::Debug {
|
||||
|
||||
// Constants //////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Length of a session ID such as [InitHello::sidi]
|
||||
pub const SESSION_ID_LEN: usize = 4;
|
||||
/// Length of a biscuit ID; i.e. size of the value in [Biscuit::biscuit_no]
|
||||
pub const BISCUIT_ID_LEN: usize = 12;
|
||||
|
||||
/// TODO: Unused, remove!
|
||||
pub const WIRE_ENVELOPE_LEN: usize = 1 + 3 + 16 + 16; // TODO verify this
|
||||
|
||||
/// Size required to fit any message in binary form
|
||||
pub const MAX_MESSAGE_LEN: usize = 2500; // TODO fix this
|
||||
|
||||
/// Recognized message types
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::msgs::MsgType;
|
||||
/// use rosenpass::msgs::MsgType as M;
|
||||
///
|
||||
/// let values = [M::InitHello, M::RespHello, M::InitConf, M::EmptyData, M::CookieReply];
|
||||
/// let values_u8 = values.map(|v| -> u8 { v.into() });
|
||||
///
|
||||
/// // Can be converted to and from u8 using [::std::convert::Into] or [::std::convert::From]
|
||||
/// for v in values.iter().copied() {
|
||||
/// let v_u8 : u8 = v.into();
|
||||
/// let v2 : MsgType = v_u8.try_into()?;
|
||||
/// assert_eq!(v, v2);
|
||||
/// }
|
||||
///
|
||||
/// // Converting an unsupported type produces an error
|
||||
/// let invalid_values = (u8::MIN..=u8::MAX)
|
||||
/// .filter(|v| !values_u8.contains(v));
|
||||
/// for v in invalid_values {
|
||||
/// let res : Result<MsgType, _> = v.try_into();
|
||||
/// assert!(res.is_err());
|
||||
/// }
|
||||
///
|
||||
/// Ok::<(), anyhow::Error>(())
|
||||
/// ```
|
||||
#[repr(u8)]
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub enum MsgType {
|
||||
/// MsgType for [InitHello]
|
||||
InitHello = 0x81,
|
||||
/// MsgType for [RespHello]
|
||||
RespHello = 0x82,
|
||||
/// MsgType for [InitConf]
|
||||
InitConf = 0x83,
|
||||
/// MsgType for [EmptyData]
|
||||
EmptyData = 0x84,
|
||||
/// MsgType for [DataMsg]
|
||||
DataMsg = 0x85,
|
||||
/// MsgType for [CookieReply]
|
||||
CookieReply = 0x86,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user