use bytes::{BufMut, Bytes, BytesMut};
use errors::{WhisperError, WhisperResult};
use nom::{IResult, rest};
use sodiumoxide::crypto::box_::{Nonce, PublicKey};
pub static HEADER_SIZE: usize = 57;
#[derive(Debug, Clone, PartialEq, Copy, Eq, Hash)]
pub enum FrameKind {
Hello = 1,
Welcome,
Initiate,
Ready,
Request,
Response,
Notification,
Termination,
}
impl FrameKind {
pub fn from(kind: u8) -> Option<FrameKind> {
match kind {
1 => Some(FrameKind::Hello),
2 => Some(FrameKind::Welcome),
3 => Some(FrameKind::Initiate),
4 => Some(FrameKind::Ready),
5 => Some(FrameKind::Request),
6 => Some(FrameKind::Response),
7 => Some(FrameKind::Notification),
255 => Some(FrameKind::Termination),
_ => None,
}
}
pub fn from_slice(kind: &[u8]) -> Option<FrameKind> {
if kind.len() != 1 {
return None;
}
FrameKind::from(kind[0])
}
}
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub struct Frame {
pub id: PublicKey,
pub nonce: Nonce,
pub kind: FrameKind,
pub payload: Bytes,
}
impl Frame {
pub fn length(&self) -> usize { HEADER_SIZE + self.payload.len() }
pub fn pack_to_buf(&self, buf: &mut BytesMut) {
buf.reserve(self.length());
buf.extend_from_slice(&self.id.0);
buf.extend_from_slice(&self.nonce.0);
buf.put_u8(self.kind as u8);
buf.extend_from_slice(&self.payload);
}
pub fn pack(&self) -> Bytes {
let mut frame = BytesMut::with_capacity(self.length());
self.pack_to_buf(&mut frame);
frame.freeze()
}
pub fn from_slice(i: &[u8]) -> WhisperResult<Frame> {
match parse_frame(i) {
IResult::Done(_, frame) => Ok(frame),
IResult::Incomplete(_) => Err(WhisperError::IncompleteFrame),
IResult::Error(_) => Err(WhisperError::BadFrame),
}
}
}
named!(parse_frame < &[u8], Frame >,
do_parse!(
pk: map_opt!(take!(32), PublicKey::from_slice) >>
nonce: map_opt!(take!(24), Nonce::from_slice) >>
kind: map_opt!(take!(1), FrameKind::from_slice) >>
payload: rest >>
({
let mut vec = Vec::with_capacity(payload.len());
vec.extend(payload.iter().cloned());
Frame {
id: pk,
nonce: nonce,
kind: kind,
payload: vec.into()
}
})
)
);
#[cfg(test)]
mod test {
use super::*;
use errors::WhisperError;
use sodiumoxide::crypto::box_::{gen_keypair, gen_nonce};
#[test]
fn pack_and_unpack() {
let frame = make_frame();
let packed_frame = frame.pack();
assert_eq!(packed_frame.len(), 60);
let parsed_frame = Frame::from_slice(&packed_frame);
assert_eq!(frame, parsed_frame.unwrap());
}
#[test]
fn frame_kind_from_slice() {
let hello = FrameKind::from_slice(&[1]).unwrap();
let welcome = FrameKind::from_slice(&[2]).unwrap();
let initiate = FrameKind::from_slice(&[3]).unwrap();
let ready = FrameKind::from_slice(&[4]).unwrap();
let request = FrameKind::from_slice(&[5]).unwrap();
let response = FrameKind::from_slice(&[6]).unwrap();
let notification = FrameKind::from_slice(&[7]).unwrap();
let termination = FrameKind::from_slice(&[255]).unwrap();
let bad = FrameKind::from_slice(&[100]);
let none = FrameKind::from_slice(&[]);
assert_eq!(hello, FrameKind::Hello);
assert_eq!(welcome, FrameKind::Welcome);
assert_eq!(initiate, FrameKind::Initiate);
assert_eq!(ready, FrameKind::Ready);
assert_eq!(request, FrameKind::Request);
assert_eq!(response, FrameKind::Response);
assert_eq!(notification, FrameKind::Notification);
assert_eq!(termination, FrameKind::Termination);
assert!(bad.is_none());
assert!(none.is_none());
}
#[test]
fn malformed_frame() {
let packed_frame = vec![1 as u8, 2, 3];
let parsed_frame = Frame::from_slice(&packed_frame);
assert_eq!(parsed_frame.is_err(), true);
let err = parsed_frame.err().unwrap();
let mut is_incomplete = false;
if let WhisperError::IncompleteFrame = err {
is_incomplete = true;
}
assert!(is_incomplete);
}
#[test]
fn bad_frame() {
let bad_frame = b"\x85\x0f\xc2?\xce\x80f\x16\xec8\x04\xc7{5\x98\xa7u<\xa5y\xda\x12\xfe\xad\xdc^%[\x8ap\xfa7q.-)\xe4V\xec\x94\xb2\x7f\r\x9a\x91\xc7\xcd\x08\xa4\xee\xbfbpH\x07%\r\0\0\0";
let result = Frame::from_slice(&bad_frame[0..59]);
assert!(result.is_err());
let err = result.err().unwrap();
let mut is_bad = false;
if let WhisperError::BadFrame = err {
is_bad = true;
}
assert!(is_bad);
}
fn make_frame() -> Frame {
let (pk, _) = gen_keypair();
let payload = vec![0, 0, 0];
let nonce = gen_nonce();
Frame {
id: pk,
nonce: nonce,
kind: FrameKind::Hello,
payload: payload.into(),
}
}
}