181 lines
5.1 KiB
Rust
181 lines
5.1 KiB
Rust
use crate::{
|
|
flags::{has, FLAG_CLOSE, FLAG_HMAC, FLAG_OPEN, FLAG_REPLY},
|
|
hmac,
|
|
params::Params,
|
|
validate_header, write_header, ProtoError, Result, HEADER_SIZE, HMAC_SIZE, TOKEN_SIZE,
|
|
};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct OpenRequest {
|
|
pub params: Params,
|
|
pub close: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct OpenReply {
|
|
pub flags: u8,
|
|
pub token: u64,
|
|
pub params: Params,
|
|
}
|
|
|
|
pub fn encode_open_request(request: &OpenRequest, hmac_key: Option<&[u8]>) -> Result<Vec<u8>> {
|
|
let mut flags = FLAG_OPEN;
|
|
if request.close {
|
|
flags |= FLAG_CLOSE;
|
|
}
|
|
if hmac_key.is_some() {
|
|
flags |= FLAG_HMAC;
|
|
}
|
|
|
|
let mut out = Vec::new();
|
|
write_header(&mut out, flags);
|
|
if hmac_key.is_some() {
|
|
out.extend_from_slice(&[0; HMAC_SIZE]);
|
|
}
|
|
out.extend_from_slice(&request.params.encode());
|
|
|
|
if let Some(key) = hmac_key {
|
|
hmac::compute_hmac_in_place(key, &mut out, hmac::hmac_offset())?;
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
pub fn decode_open_reply(packet: &[u8], hmac_key: Option<&[u8]>) -> Result<OpenReply> {
|
|
let flags = validate_header(packet)?;
|
|
require(flags, FLAG_OPEN)?;
|
|
require(flags, FLAG_REPLY)?;
|
|
check_hmac_presence(flags, hmac_key)?;
|
|
if let Some(key) = hmac_key {
|
|
hmac::verify_hmac(key, packet, hmac::hmac_offset())?;
|
|
}
|
|
|
|
let mut pos = HEADER_SIZE;
|
|
if has(flags, FLAG_HMAC) {
|
|
pos += HMAC_SIZE;
|
|
}
|
|
let needed = pos + TOKEN_SIZE;
|
|
if packet.len() < needed {
|
|
return Err(ProtoError::PacketTooShort {
|
|
needed,
|
|
actual: packet.len(),
|
|
});
|
|
}
|
|
let token = u64::from_le_bytes(packet[pos..pos + TOKEN_SIZE].try_into().unwrap());
|
|
pos += TOKEN_SIZE;
|
|
if token == 0 && !has(flags, FLAG_CLOSE) {
|
|
return Err(ProtoError::ZeroToken);
|
|
}
|
|
|
|
Ok(OpenReply {
|
|
flags,
|
|
token,
|
|
params: Params::decode(&packet[pos..])?,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn require(flags: u8, flag: u8) -> Result<()> {
|
|
if has(flags, flag) {
|
|
Ok(())
|
|
} else {
|
|
Err(ProtoError::MissingFlag(flag))
|
|
}
|
|
}
|
|
|
|
pub(crate) fn reject(flags: u8, flag: u8) -> Result<()> {
|
|
if has(flags, flag) {
|
|
Err(ProtoError::UnexpectedFlag(flag))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub(crate) fn check_hmac_presence(flags: u8, hmac_key: Option<&[u8]>) -> Result<()> {
|
|
if has(flags, FLAG_HMAC) == hmac_key.is_some() {
|
|
Ok(())
|
|
} else {
|
|
Err(ProtoError::HmacPresenceMismatch)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::params::{Clock, ReceivedStats, StampAt};
|
|
|
|
fn default_params() -> Params {
|
|
Params {
|
|
protocol_version: 1,
|
|
duration_ns: 3_000_000_000,
|
|
interval_ns: 1_000_000_000,
|
|
received_stats: ReceivedStats::Both,
|
|
stamp_at: StampAt::Both,
|
|
clock: Clock::Both,
|
|
..Params::default()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn open_request_has_no_token() {
|
|
let packet = encode_open_request(
|
|
&OpenRequest {
|
|
params: default_params(),
|
|
close: false,
|
|
},
|
|
None,
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(packet.len(), 24);
|
|
assert_eq!(&packet[..4], &[0x14, 0xa7, 0x5b, 0x01]);
|
|
assert_eq!(&packet[4..], &default_params().encode());
|
|
}
|
|
|
|
#[test]
|
|
fn hmac_open_request_inserts_hmac_before_params() {
|
|
let packet = encode_open_request(
|
|
&OpenRequest {
|
|
params: default_params(),
|
|
close: false,
|
|
},
|
|
Some(b"testkey"),
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
packet.len(),
|
|
4 + HMAC_SIZE + default_params().encode().len()
|
|
);
|
|
assert_eq!(&packet[..4], &[0x14, 0xa7, 0x5b, FLAG_OPEN | FLAG_HMAC]);
|
|
assert_eq!(&packet[4 + HMAC_SIZE..], &default_params().encode());
|
|
hmac::verify_hmac(b"testkey", &packet, hmac::hmac_offset()).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn open_reply_decodes_token() {
|
|
let mut packet = vec![0x14, 0xa7, 0x5b, 0x03];
|
|
packet.extend_from_slice(&0x7896_b6ab_8771_5213u64.to_le_bytes());
|
|
packet.extend_from_slice(&default_params().encode());
|
|
|
|
let reply = decode_open_reply(&packet, None).unwrap();
|
|
assert_eq!(reply.token, 0x7896_b6ab_8771_5213);
|
|
assert_eq!(reply.params, default_params());
|
|
}
|
|
|
|
#[test]
|
|
fn hmac_open_reply_decodes_token_after_hmac_field() {
|
|
let mut packet = vec![0x14, 0xa7, 0x5b, FLAG_OPEN | FLAG_REPLY | FLAG_HMAC];
|
|
packet.extend_from_slice(&[0; HMAC_SIZE]);
|
|
packet.extend_from_slice(&0x7896_b6ab_8771_5213u64.to_le_bytes());
|
|
packet.extend_from_slice(&default_params().encode());
|
|
hmac::compute_hmac_in_place(b"testkey", &mut packet, hmac::hmac_offset()).unwrap();
|
|
|
|
assert_eq!(
|
|
packet.len(),
|
|
4 + HMAC_SIZE + TOKEN_SIZE + default_params().encode().len()
|
|
);
|
|
let reply = decode_open_reply(&packet, Some(b"testkey")).unwrap();
|
|
assert_eq!(reply.token, 0x7896_b6ab_8771_5213);
|
|
assert_eq!(reply.params, default_params());
|
|
}
|
|
}
|