Skip to content

Commit

Permalink
Use nom for parsing. Fixes datrs#3
Browse files Browse the repository at this point in the history
  • Loading branch information
khernyo committed May 14, 2018
1 parent 7f0fe2b commit f803af2
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ readme = "README.md"
[dependencies]
failure = "0.1.1"
byteorder = "1.2.1"

[dependencies.nom]
version = "~3.2.1"
features = ["verbose-errors"]
11 changes: 9 additions & 2 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ extern crate byteorder;

use self::byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use failure::Error;
use nom::IResult;
use parsers;
use std::io::Cursor;

/// Algorithm used for hashing the data.
Expand Down Expand Up @@ -42,14 +44,14 @@ pub enum FileType {
}

/// SLEEP Protocol version.
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum ProtocolVersion {
/// The version specified as per the paper released in 2017-09.
V0,
}

/// Structural representation of 32 byte SLEEP headers.
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct Header {
/// Type of file.
pub file_type: FileType,
Expand All @@ -76,6 +78,11 @@ impl Header {
}
}

/// Parses a 32 byte buffer slice into a valid Header.
pub fn from_bytes(buf: &[u8]) -> IResult<&[u8], Header> {
parsers::header(buf)
}

/// Parse a 32 byte buffer slice into a valid Header.
pub fn from_vec(buffer: &[u8]) -> Result<Header, Error> {
ensure!(buffer.len() == 32, "buffer should be 32 bytes");
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

#[macro_use]
extern crate failure;
#[macro_use]
extern crate nom;

mod header;
mod parsers;

pub use header::*;

Expand Down
131 changes: 131 additions & 0 deletions src/parsers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use header::*;
use nom::{be_u16, be_u8, rest};
use std::str;

const HEADER_LENGTH: usize = 32;
const VERIFY_TRAILING_ZEROS: bool = true;

named!(
file_type<FileType>,
switch!(be_u8,
0 => value!(FileType::BitField) |
1 => value!(FileType::Signatures) |
2 => value!(FileType::Tree)
)
);

named!(
protocol_version<ProtocolVersion>,
switch!(be_u8,
0 => value!(ProtocolVersion::V0)
)
);

named!(
pub header<Header>,
flat_map!(
take!(HEADER_LENGTH),
do_parse!(
tag!(b"\x05\x02\x57") >>
file_type: file_type >>
protocol_version: protocol_version >>
entry_size: be_u16 >>

algorithm_len: be_u8 >>
algorithm: switch!(map_res!(take!(algorithm_len), str::from_utf8),
"BLAKE2b" => value!(HashType::BLAKE2b) |
"Ed25519" => value!(HashType::Ed25519) |
"" => value!(HashType::None)
) >>

verify!(rest, |bytes: &[u8]| {
let header_consumed = bytes.len() + algorithm_len as usize + 8 == HEADER_LENGTH;
let trailing_zeros = !VERIFY_TRAILING_ZEROS || bytes.iter().all(|&b| b == 0u8);
header_consumed && trailing_zeros
}) >>

(Header {
file_type,
protocol_version,
entry_size,
hash_type: algorithm,
})
)
)
);

#[cfg(test)]
mod test {
use super::*;

use nom::IResult::Done;

#[test]
fn parse_file_type() {
assert_eq!(
file_type(b"\x00"),
Done(&[][..], FileType::BitField)
);
assert_eq!(
file_type(b"\x01"),
Done(&[][..], FileType::Signatures)
);
assert_eq!(
file_type(b"\x02"),
Done(&[][..], FileType::Tree)
);
assert!(file_type(b"\xff").is_err());
}

#[test]
fn parse_header() {
fn mk_header(prefix: &[u8]) -> [u8; 32] {
let mut h = [0u8; 32];
h[0..prefix.len()].clone_from_slice(prefix);
h
}

assert_eq!(
header(&mk_header(
b"\x05\x02W\x01\x00\x00\x28\x07BLAKE2b"
)),
Done(
&[][..],
Header {
file_type: FileType::Signatures,
protocol_version: ProtocolVersion::V0,
entry_size: 40,
hash_type: HashType::BLAKE2b
}
)
);
assert_eq!(
header(&mk_header(
b"\x05\x02W\x01\x00\x00\x28\x07BLAKE2b"
)).unwrap()
.1
.hash_type,
HashType::BLAKE2b
);
assert_eq!(
header(&mk_header(
b"\x05\x02W\x01\x00\x00\x28\x07Ed25519"
)).unwrap()
.1
.hash_type,
HashType::Ed25519
);
assert_eq!(
header(&mk_header(b"\x05\x02W\x01\x00\x00\x28\x00"))
.unwrap()
.1
.hash_type,
HashType::None
);
assert!(header(&mk_header(b"\x05\x02W\x01\x00\x00\x28\x01B")).is_err());
assert!(header(&mk_header(b"\x05\x02W\x01\x00\x00\x28\x01B")).is_err());

let h = b"\x05\x02W\x01\x00\x00\x28\x19BLAKE2bXXXXXXXXXXXXXXXXXX";
assert!(header(h).is_incomplete());
}
}
14 changes: 14 additions & 0 deletions tests/invalid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extern crate sleep_parser;

use sleep_parser::*;

#[test]
fn issue_3() {
// https://github.com/datrs/sleep-parser/issues/3

let data = b"\x05\x02W\x01\x00\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xfb\x03p\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xbb9\xb0\xf5\xf5";
assert!(Header::from_bytes(data).is_incomplete());

let data = b"\x05\x02W\x01\x00\x00\x00\x12\x12\x12\x00\x00S\xc3\xcf\x8a2\xcc\xd1\xce9\xc4K\x9343\x00602\xb5\x07";
assert!(Header::from_bytes(data).is_err());
}

0 comments on commit f803af2

Please sign in to comment.