diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index 79bf9da6..d0bbad35 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -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::::new_zeroed(); +/// +/// // Edit fields normally +/// ih.mac[0] = 1; +/// +/// // Edit as binary +/// ih.as_bytes_mut()[offset_of!(Envelope, 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>::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 { @@ -40,6 +81,40 @@ pub struct Envelope { 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::::new_zeroed(); +/// +/// // Conversion to byte representation +/// let ih = ih.as_bytes_mut(); +/// +/// // Set value on byte representation +/// ih[span_of!(Envelope, payload)][span_of!(InitHello, sidi)] +/// .copy_from_slice(&[1,2,3,4]); +/// +/// // Conversion from bytes +/// let ih = Ref::<&mut [u8], Envelope>::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::::new_zeroed(); +/// +/// // Conversion to byte representation +/// let ih = ih.as_bytes_mut(); +/// +/// // Set value on byte representation +/// ih[span_of!(Envelope, payload)][span_of!(RespHello, sidi)] +/// .copy_from_slice(&[1,2,3,4]); +/// +/// // Conversion from bytes +/// let ih = Ref::<&mut [u8], Envelope>::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::::new_zeroed(); +/// +/// // Conversion to byte representation +/// let ih = ih.as_bytes_mut(); +/// +/// // Set value on byte representation +/// ih[span_of!(Envelope, payload)][span_of!(InitConf, sidi)] +/// .copy_from_slice(&[1,2,3,4]); +/// +/// // Conversion from bytes +/// let ih = Ref::<&mut [u8], Envelope>::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::::new_zeroed(); +/// +/// // Conversion to byte representation +/// let ih = ih.as_bytes_mut(); +/// +/// // Set value on byte representation +/// ih[span_of!(Envelope, payload)][span_of!(EmptyData, sid)] +/// .copy_from_slice(&[1,2,3,4]); +/// +/// // Conversion from bytes +/// let ih = Ref::<&mut [u8], Envelope>::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 = 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, }