diff --git a/Cargo.toml b/Cargo.toml index bafcd16..a2047a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,4 +25,5 @@ thiserror = "1.0.30" parking_lot = "0.12.0" x25519-dalek = "2.0.0-pre.1" rand_core = "0.6.3" -sp-keyring = "5.0.0" \ No newline at end of file +sp-keyring = "5.0.0" +regex = "1.5.5" diff --git a/src/lib.rs b/src/lib.rs index 8c8d501..f5377d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod database; pub mod dh_tools; pub mod fennel; pub mod rsa_tools; +pub mod whiteflag; use codec::{Decode, Encode}; @@ -11,6 +12,7 @@ pub use crate::database::*; pub use crate::dh_tools::*; pub use crate::fennel::*; pub use crate::rsa_tools::*; +pub use crate::whiteflag::*; /// The FennelServerPacket struct is used for carrying formatted messages between the server and client. #[derive(Copy, Clone, Encode, Decode, Debug)] diff --git a/src/whiteflag/mod.rs b/src/whiteflag/mod.rs new file mode 100644 index 0000000..af9321a --- /dev/null +++ b/src/whiteflag/mod.rs @@ -0,0 +1,5 @@ +#[cfg(test)] +mod tests; + +pub mod wf_codec; +pub mod wf_core; diff --git a/src/whiteflag/tests.rs b/src/whiteflag/tests.rs new file mode 100644 index 0000000..ae6c5b1 --- /dev/null +++ b/src/whiteflag/tests.rs @@ -0,0 +1,69 @@ +use crate::whiteflag::wf_core::message::WhiteflagMessage; + +#[cfg(test)] +#[test] +fn test_create_new_message() { + let mut message = WhiteflagMessage::new("S".to_string()); + assert_eq!(message.message_type, "S"); + assert!(message.is_valid()); + + assert_eq!("WF", message.prefix); + assert_eq!("1", message.version); + assert_eq!("S", message.message_code); + + assert!(message.set_encryption_indicator("1".to_string())); + assert!(!message.set_encryption_indicator("2".to_string())); + + /* Verify body fields */ + assert!(message.set_subject_code("10".to_string())); + assert!(!message.set_subject_code("20".to_string())); + assert!(message.set_object_type("21".to_string())); + assert!(!message.set_object_type("22".to_string())); + + /* Verify metadata */ + assert_eq!(None, message.set_transaction_hash("a1b2c3".to_string())); + assert_eq!( + "a1b2c3", + message.set_transaction_hash("d4e5f6".to_string()).unwrap() + ); + assert_eq!(None, message.set_originator_address("abc123".to_string())); + assert_eq!("abc123", message.get_originator_address()); +} + +#[test] +fn test_compile_auth_message() { + let field_values = vec![ + "WF", + "1", + "0", + "0", + "A", + "0", + "0000000000000000000000000000000000000000000000000000000000000000", + "1", + "b01218a30dd3c23d050af254bfcce31a715fecdff6a23fd59609612e6e0ef263", + ]; + + let message = WhiteflagMessage::compile_auth_message(field_values.clone()).unwrap(); + + assert_eq!("A", message.message_type()); + assert_eq!(field_values[0], message.prefix()); + assert_eq!(field_values[1], message.version()); + assert_eq!(field_values[2], message.get_encryption_indicator()); + assert_eq!(field_values[3], message.duress_indictor()); + assert_eq!(field_values[4], message.message_code()); + assert_eq!(field_values[5], message.reference_indicator()); + assert_eq!(field_values[6], message.referenced_message()); + assert_eq!(field_values[7], message.verification_method()); + assert_eq!(field_values[8], message.verification_data()); + assert!(message.is_valid()); +} + +#[test] +fn test_serialize_auth_message() {} + +#[test] +fn test_deserialize_auth_message() {} + +#[test] +fn test_decode_auth_message() {} diff --git a/src/whiteflag/wf_codec/binary.rs b/src/whiteflag/wf_codec/binary.rs new file mode 100644 index 0000000..b406e13 --- /dev/null +++ b/src/whiteflag/wf_codec/binary.rs @@ -0,0 +1,44 @@ +use super::constants::BYTE; + +pub fn encode_binary>(binary_str: T) -> Vec { + let bit_length: usize = binary_str.as_ref().len(); + let byte_length: usize = (bit_length / BYTE) + + (match bit_length % BYTE == 0 { + true => 0, + false => 1, + }); + let mut buffer = vec![0; byte_length]; + + for bit_index in 0..bit_length { + if binary_str + .as_ref() + .chars() + .nth(bit_index) + .expect("something wrong") + == '1' + { + let byte_cursor: usize = bit_index / BYTE; + let bit_position: usize = bit_index % BYTE; + buffer[byte_cursor] |= 0x80 >> bit_position; + } + } + + buffer +} + +pub fn decode_binary(buffer: &[u8], bit_length: usize) -> String { + let mut data: Vec = Vec::new(); + + for bit_index in 0..bit_length { + let byte_cursor: usize = bit_index / BYTE; + let bit_position: usize = bit_index % BYTE; + + if (buffer[byte_cursor] >> (BYTE - bit_position - 1) & 1) == 1 { + data.push('1'); + } else { + data.push('0'); + } + } + + data.into_iter().collect() //to lower? +} diff --git a/src/whiteflag/wf_codec/common.rs b/src/whiteflag/wf_codec/common.rs new file mode 100644 index 0000000..a12e97c --- /dev/null +++ b/src/whiteflag/wf_codec/common.rs @@ -0,0 +1,310 @@ +use super::constants::*; + +//https://github.com/WhiteflagProtocol/whiteflag-java/blob/57db4b6963a4a7913afdeb596e7ce11d46d9d93b/src/main/java/org/whiteflagprotocol/java/core/WfBinaryBuffer.java#L299 +pub fn to_hex(data: &Vec) -> String { + data.iter().flat_map(|b| convert_byte_to_hex(*b)).collect() +} + +fn convert_byte_to_hex(byte: u8) -> [char; 2] { + let byte_u32 = byte as u32; + let c1 = std::char::from_digit((byte_u32 >> QUADBIT) & 0xF, HEXRADIX as u32) + .expect("encoding failed"); + let c2 = std::char::from_digit(byte_u32 & 0xF, HEXRADIX as u32).expect("encoding failed"); + [c1, c2] +} + +/** + * decodes a hexadecimal string into a buffer and includes bit_length + * the equivalent to WfBinaryBuffer.convertToByteArray in whiteflag java + */ +pub fn decode_from_hexadecimal>(data: T) -> (Vec, usize) { + let buffer = hex::decode(remove_hexadecimal_prefix(data.as_ref())).unwrap(); + let bit_length = buffer.len() * BYTE; + (buffer, bit_length) +} + +/** + * Converts a hexadecimal string to a byte array + * @param hexstr the hexadecimal string + * @return a byte array + * @throws IllegalArgumentException if argument is not a hexadecimal string + */ +pub fn from_hex>(hex: T) -> Vec { + let mut cleaned_hex = remove_hexadecimal_prefix(hex.as_ref()).to_string(); + + if cleaned_hex.len() % 2 == 1 { + cleaned_hex += "0"; + } + + /* if (!HEXPATTERN.matcher(str).matches()) { + throw new IllegalArgumentException("Invalid hexadecimal string"); + } */ + + /* Loop through hexadecimal string and take two chars at a time*/ + let data: Vec = cleaned_hex.chars().into_iter().collect(); + //let data = cleaned_hex.as_bytes(); + let str_length = data.len(); + let mut buffer = vec![0; str_length / 2]; + + for i in (0..str_length).step_by(2) { + buffer[i / 2] = (u8::from_str_radix(&data[i].to_string(), HEXRADIX as u32) + .expect("conversion error") + << QUADBIT) + + u8::from_str_radix(&data[i + 1].to_string(), HEXRADIX as u32) + .expect("conversion error"); + } + + buffer +} + +/** + * removes characters from string that are invalid in hexadecimal format + */ +pub fn remove_all_invalid_hex_characters>(data: T) -> String { + let re = regex::Regex::new("[-+:.A-Z]").unwrap(); + re.replace_all(data.as_ref(), "").to_string() +} + +pub fn remove_hexadecimal_prefix(data: &str) -> &str { + if data.starts_with("0x") { + return &data[2..]; + } + + data +} + +/** + * Calculates the number of bytes required to hold the given number of bits + */ +pub fn byte_length(bit_length: isize) -> isize { + let i_byte = BYTE as isize; + (bit_length / i_byte) + (if (bit_length % i_byte) > 0 { 1 } else { 0 }) +} + +/** + * Shortens the byte array to fit the length of the used bits + */ +pub fn crop_bits(buffer: Vec, bit_length: isize) -> Vec { + if bit_length == 0 { + return buffer; + } + + let is_positive = bit_length > 0; + let u_bit_length = bit_length as usize; + + let (byte_length, clear_bits) = match is_positive { + true => { + let length = byte_length(bit_length); + if length > buffer.len() as isize { + return buffer[0..length as usize].to_vec(); + } + (length as usize, BYTE - (u_bit_length % BYTE)) + } + false => { + let length: isize = buffer.len() as isize + (bit_length / (BYTE as isize)); + if length < 1 { + return vec![0]; + } + (length as usize, u_bit_length) + } + }; + + let mut cropped_buffer = buffer[0..byte_length].to_vec(); + + /* Clear unused bits in last byte */ + if clear_bits < BYTE { + cropped_buffer[byte_length - 1] &= 0xFF << clear_bits; + } + + cropped_buffer +} + +/** + * Shifts bits in a byte array to the right modulo 8 + */ +pub fn shift_right(buffer: &[u8], shift: isize) -> Vec { + if shift < 0 { + return shift_left(buffer, -shift); + } + + let modulate: usize = shift as usize % BYTE; + + if modulate == 0 { + return buffer.to_vec(); + } + + let mask: u8 = 0xFF >> (BYTE - modulate); + let length = buffer.len() + 1; + let mut new_buffer = vec![0; length]; + + for i in (1..length).rev() { + let b = &buffer[i - 1]; + new_buffer[i] |= (0xFF & b & mask) << (BYTE - modulate); + new_buffer[i - 1] = (0xFF & b) >> modulate; + } + + new_buffer +} + +/** + * Shifts bits in a byte array to the left modulo 8 + */ +pub fn shift_left(buffer: &[u8], shift: isize) -> Vec { + if shift < 0 { + return shift_right(buffer, -shift); + } + + let modulate: usize = shift as usize % BYTE; + + if modulate == 0 { + return buffer.to_vec(); + } + + let mask: u8 = 0xFF << (BYTE - modulate); + let length = buffer.len(); + let mut new_buffer = vec![0; length]; + + for i in 0..length { + new_buffer[i] = (0xFF & buffer[i]) << modulate; + if i < length - 1 { + new_buffer[i] |= (0xFF & buffer[i + 1] & mask) >> (BYTE - modulate); + } + } + + crop_bits(new_buffer, -(shift % BYTE as isize)) +} + +/** + * Returns a byte array with a subset of the bits in the buffer + * @param startBit the first bit of the subset to extract + * @param bitLength the length of the subset, i.e. the number of bits to extract + * @return a byte array with the extracted bits + */ +pub fn extract_bits( + buffer: &[u8], + buffer_bit_length: usize, + start_bit: usize, + mut bit_length: usize, +) -> Vec { + if bit_length < 1 || bit_length > (buffer_bit_length - start_bit) { + bit_length = buffer_bit_length - start_bit; + } + + let start_byte = start_bit / BYTE; + let byte_length = byte_length(bit_length as isize) as usize; + let shift = start_bit % BYTE; + let mask: u8 = (BYTE - shift).checked_shl(0xFF).unwrap_or(u8::MAX as usize) as u8; + + let mut new_byte_array: Vec = vec![0; byte_length]; + if shift == 0 { + /* Faster loop if no shift needed */ + for byte_index in 0..byte_length { + new_byte_array[byte_index] = buffer[start_byte + byte_index]; + } + } else { + /* Loop through bytes to shift */ + for byte_index in 0..byte_length { + new_byte_array[byte_index] = (0xFF & buffer[start_byte + byte_index]) << shift; + } + + let end_byte = if byte_length < (buffer.len() - start_byte) { + byte_length + } else { + byte_length - 1 + }; + + for byte_index in 0..end_byte { + new_byte_array[byte_index] |= + (0xFF & buffer[start_byte + byte_index + 1] & mask) >> (BYTE - shift); + } + } + + crop_bits(new_byte_array, bit_length as isize) +} + +/** + * Appends the specified number of bits from a bytes array to the binary buffer + * @param byteArray the byte array with the bits to be appended + * @param nBits the number of bits to be appended from the byte array + * @return this binary buffer + * @throws IllegalStateException if the buffer is marked complete and cannot be altered + */ +pub fn append_bits( + buffer_1: &[u8], + len_1: usize, + buffer_2: &[u8], + mut len_2: usize, +) -> (Vec, usize) { + /* Check number of bits */ + let max_number_of_bits = buffer_2.len() * BYTE; + if len_2 > max_number_of_bits { + len_2 = max_number_of_bits; + } + + /* Add bits to the end of the buffer */ + let new_buffer = concatinate_bits(&buffer_1, len_1, &buffer_2, len_2); + + (new_buffer, len_1 + len_2) +} + +/** + * Concatinates two bitsets + * @param byte_array_1 byte array containing the first bitset + * @param n_bits_1 number of bits in the first bitset, i.e. which bits to take from the first byte array + * @param byte_array_2 byte array containing the second bitset + * @param n_bits_2 number of bits in the second bitset, i.e. which bits to take from the second byte array + * @return a new byte array with the concatinated bits + */ +pub fn concatinate_bits( + byte_array_1: &[u8], + mut n_bits_1: usize, + byte_array_2: &[u8], + mut n_bits_2: usize, +) -> Vec { + /* Check number of bits */ + if n_bits_1 > (byte_array_1.len() * BYTE) { + n_bits_1 = byte_array_1.len() * BYTE; + } + + if n_bits_2 > (byte_array_2.len() * BYTE) { + n_bits_2 = byte_array_2.len() * BYTE; + } + + /* Calculate parameters */ + let shift = n_bits_1 % BYTE; + let free_bits = if shift == 0 { 0 } else { BYTE - shift }; + let byte_length_1 = (n_bits_1 / BYTE) + (if free_bits == 0 { 0 } else { 1 }); + let bit_length = n_bits_1 + n_bits_2; + let byte_length = byte_length(bit_length as isize) as usize; + + /* Prepare byte arrays */ + let byte_array_2_shift = shift_right(&byte_array_2, shift as isize); + let mut new_byte_array = vec![0; byte_length as usize]; + + /* Concatination */ + let mut byte_cursor = 0; + let mut start_byte_2 = 0; + if byte_length_1 != 0 { + /* Add first byte array */ + for byte_index in 0..byte_length_1 { + byte_cursor = byte_index; + new_byte_array[byte_cursor] = byte_array_1[byte_index]; + } + + /* Add overlapping byte from second byte array*/ + if free_bits > 0 { + new_byte_array[byte_cursor] |= byte_array_2_shift[0]; + start_byte_2 = 1; + } + byte_cursor += 1; + } + /* Add the rest of the second byte array */ + let end_byte_2 = start_byte_2 + byte_length - byte_cursor; + + for byte_index in start_byte_2..end_byte_2 { + new_byte_array[byte_cursor] = byte_array_2_shift[byte_index]; + byte_cursor += 1; + } + + return crop_bits(new_byte_array, bit_length as isize); +} diff --git a/src/whiteflag/wf_codec/constants.rs b/src/whiteflag/wf_codec/constants.rs new file mode 100644 index 0000000..55149c7 --- /dev/null +++ b/src/whiteflag/wf_codec/constants.rs @@ -0,0 +1,9 @@ +pub const HEXPATTERN: &str = "^[a-fA-F0-9]*$"; +pub const BINPREFIX: &str = "0b"; +pub const HEXPREFIX: &str = "0x"; +pub const BINRADIX: usize = 2; +pub const HEXRADIX: usize = 16; +pub const BYTE: usize = 8; +pub const OCTET: usize = 8; +pub const QUADBIT: usize = 4; +pub const BIT: usize = 1; diff --git a/src/whiteflag/wf_codec/encoding.rs b/src/whiteflag/wf_codec/encoding.rs new file mode 100644 index 0000000..63af27c --- /dev/null +++ b/src/whiteflag/wf_codec/encoding.rs @@ -0,0 +1,185 @@ +use super::binary::{decode_binary, encode_binary}; +use super::common::{remove_all_invalid_hex_characters, shift_left}; +use super::constants::*; +use super::hexadecimal::{decode_bdx, encode_bdx}; +use super::latlong::encode_latlong; + +#[derive(Clone)] +pub struct Encoding { + pub charset: &'static str, + pub bit_length: usize, + pub byte_length: Option, + pub kind: EncodingKind, +} + +#[derive(Clone)] +pub enum EncodingKind { + BIN, + DEC, + HEX, + UTF8, + DATETIME, + DURATION, + LAT, + LONG, +} + +impl Encoding { + fn new( + charset: &'static str, + bit_length: usize, + byte_length: Option, + kind: EncodingKind, + ) -> Encoding { + Encoding { + charset, + bit_length, + byte_length, + kind, + } + } + + pub fn encode + std::fmt::Display>(&self, value: T) -> Vec { + match &self.kind { + EncodingKind::UTF8 => value.as_ref().as_bytes().to_vec(), + EncodingKind::BIN => encode_binary(value), + EncodingKind::DEC | EncodingKind::HEX => encode_bdx(value), + EncodingKind::DATETIME | EncodingKind::DURATION => { + encode_bdx(remove_all_invalid_hex_characters(value)) + } + EncodingKind::LAT | EncodingKind::LONG => encode_latlong(value), + } + } + + pub fn decode(&self, buffer: Vec, bit_length: usize) -> String { + let mut s = String::new(); + + match &self.kind { + EncodingKind::UTF8 => { + return String::from_utf8(buffer).expect("utf8 error"); + } + EncodingKind::BIN => { + return decode_binary(&buffer, bit_length); + } + EncodingKind::DEC | EncodingKind::HEX => { + return decode_bdx(buffer, bit_length); + } + EncodingKind::DATETIME => { + s.push_str(decode_bdx(buffer, bit_length).as_str()); + + s.insert(4, '-'); + s.insert(7, '-'); + s.insert(10, 'T'); + s.insert(13, ':'); + s.insert(16, ':'); + s.insert(19, 'Z'); + } + EncodingKind::DURATION => { + s.push_str(decode_bdx(buffer, bit_length).as_str()); + + s.insert(0, 'P'); + s.insert(3, 'D'); + s.insert(6, 'H'); + s.insert(9, 'M'); + } + EncodingKind::LAT | EncodingKind::LONG => { + let sign = if ((buffer[0] >> (BYTE - 1)) & 1) == 1 { + '+' + } else { + '-' + }; + + s.push(sign); + s.push_str(decode_bdx(shift_left(&buffer, 1), bit_length - 1).as_str()); + s.insert(s.len() - 5, '.'); + } + } + + s + } + + pub fn is_fixed_length(&self) -> bool { + self.byte_length != None + } + + /** + * Returns the bit length of a field for a given encoding and unencoded field byte length + * @param byteLength the number of bytes in the unencoded field + * @return the number of bits in a compressed encoded field + */ + pub fn bit_length(&self, byte_length: usize) -> usize { + if self.is_fixed_length() { + return self.bit_length; + } + byte_length * self.bit_length + } +} + +pub const BIN: Encoding = Encoding { + charset: "[01]", + bit_length: BIT, + byte_length: None, + kind: EncodingKind::BIN, +}; + +pub const DEC: Encoding = Encoding { + charset: "[0-9]", + bit_length: QUADBIT, + byte_length: None, + kind: EncodingKind::DEC, +}; + +pub const HEX: Encoding = Encoding { + charset: "[a-fA-F0-9]", + bit_length: QUADBIT, + byte_length: None, + kind: EncodingKind::HEX, +}; + +pub const UTF8: Encoding = Encoding { + charset: r"[\u0000-\u007F]", + bit_length: OCTET, + byte_length: None, + kind: EncodingKind::UTF8, +}; + +pub const DATETIME: Encoding = Encoding { + charset: "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", + bit_length: 56, + byte_length: Some(20), + kind: EncodingKind::DATETIME, +}; + +pub const DURATION: Encoding = Encoding { + charset: "P[0-9]{2}D[0-9]{2}H[0-9]{2}M", + bit_length: 24, + byte_length: Some(10), + kind: EncodingKind::DURATION, +}; + +pub const LAT: Encoding = Encoding { + charset: "[+\\-][0-9]{2}\\.[0-9]{5}", + bit_length: 29, + byte_length: Some(9), + kind: EncodingKind::LAT, +}; + +pub const LONG: Encoding = Encoding { + charset: "[+\\-][0-9]{3}\\.[0-9]{5}", + bit_length: 33, + byte_length: Some(10), + kind: EncodingKind::LONG, +}; + +/* protected final WfBinaryBuffer encode() throws WfCoreException { + WfBinaryBuffer buffer = WfBinaryBuffer.create(); + int byteCursor = fields[0].startByte; + for (WfMessageField field : fields) { + if (field.startByte != byteCursor) { + throw new WfCoreException("Invalid field order while encoding: did not expect field " + field.debugInfo() + " at byte " + byteCursor, null); + } + buffer.addMessageField(field); + byteCursor = field.endByte; + } + return buffer; +} */ diff --git a/src/whiteflag/wf_codec/hexadecimal.rs b/src/whiteflag/wf_codec/hexadecimal.rs new file mode 100644 index 0000000..be370c6 --- /dev/null +++ b/src/whiteflag/wf_codec/hexadecimal.rs @@ -0,0 +1,44 @@ +use super::constants::{BYTE, HEXRADIX, QUADBIT}; + +/// Encodes a (hexa)decimal string into a binary buffer +pub fn encode_bdx>(raw_input_str: T) -> Vec { + let mut data: Vec = raw_input_str.as_ref().chars().collect(); + if data.len() % 2 == 1 { + data.push('0'); + } + + let input_length = data.len(); + let mut buffer = vec![0; input_length / 2]; + + for i in (0..input_length).step_by(2) { + let digit0: u8 = to_hex_digit(data.get(i)); + let digit1: u8 = to_hex_digit(data.get(i + 1)); + buffer[i / 2] = (digit0 << QUADBIT) + digit1; + } + + buffer +} + +fn to_hex_digit(data: Option<&char>) -> u8 { + u8::from_str_radix(&data.expect("out of bounds").to_string(), HEXRADIX as u32) + .expect("failed to convert to digit") +} + +fn from_hex_digit(data: u8) -> char { + std::char::from_digit(data as u32, HEXRADIX as u32).expect("failed to convert to char") +} + +/// Decodes a binary buffer into a (hexa)decimal string +pub fn decode_bdx(buffer: Vec, bit_length: usize) -> String { + let mut hexadecimal_string: Vec = Vec::new(); + + for bit_index in (0..bit_length).step_by(BYTE) { + let byte_cursor = bit_index as usize / BYTE; + hexadecimal_string.push(from_hex_digit((buffer[byte_cursor] >> QUADBIT) & 0xF)); + if (bit_index + QUADBIT) < bit_length { + hexadecimal_string.push(from_hex_digit(buffer[byte_cursor] & 0xF)); + } + } + + hexadecimal_string.into_iter().collect() +} diff --git a/src/whiteflag/wf_codec/latlong.rs b/src/whiteflag/wf_codec/latlong.rs new file mode 100644 index 0000000..60cb277 --- /dev/null +++ b/src/whiteflag/wf_codec/latlong.rs @@ -0,0 +1,25 @@ +use super::common::{crop_bits, remove_all_invalid_hex_characters, shift_right}; +use super::constants::QUADBIT; +use super::hexadecimal::encode_bdx; + +/** + * Encodes a datum string into binary buffer + */ +pub fn encode_latlong>(data: T) -> Vec { + let input = data.as_ref(); + let cleaned_input = remove_all_invalid_hex_characters(&input); + let length = &cleaned_input.len(); + let mut buffer = encode_bdx(cleaned_input); + + if &input[0..1] == "-" { + buffer = shift_right(&buffer, 1); + } + + if &input[0..1] == "+" { + buffer = shift_right(&buffer, 1); + buffer[0] |= 0x80; + } + + let bit_length = 1 + length * QUADBIT; + crop_bits(buffer, bit_length as isize) +} diff --git a/src/whiteflag/wf_codec/mod.rs b/src/whiteflag/wf_codec/mod.rs new file mode 100644 index 0000000..aad5227 --- /dev/null +++ b/src/whiteflag/wf_codec/mod.rs @@ -0,0 +1,9 @@ +#[cfg(test)] +mod tests; + +pub mod binary; +pub mod common; +pub mod constants; +pub mod encoding; +pub mod hexadecimal; +pub mod latlong; diff --git a/src/whiteflag/wf_codec/tests.rs b/src/whiteflag/wf_codec/tests.rs new file mode 100644 index 0000000..9131a5d --- /dev/null +++ b/src/whiteflag/wf_codec/tests.rs @@ -0,0 +1,134 @@ +use crate::{ + wf_codec::common::{concatinate_bits, to_hex}, + whiteflag::wf_codec::*, +}; + +fn assert_array_eq(l: Vec, r: Vec) { + let success = l.iter().eq(r.iter()); + if !success { + println!("expected: {:?}\nwas: {:?}", l, r); + } + assert!(success); +} + +#[test] +fn test_shift_right_0() { + let original: Vec = vec![0x53, 0x7D]; + let shifted_bytes = common::shift_right(&original.clone(), 0); + + assert_array_eq(original, shifted_bytes); +} + +#[test] +fn test_shift_right_1() { + let original: Vec = vec![0x53, 0x7D]; + let expected: Vec = vec![0x0A, 0x6F, 0xA0]; + + assert_array_eq(expected.clone(), common::shift_right(&original.clone(), 3)); + assert_array_eq(expected.clone(), common::shift_left(&original.clone(), -3)); +} + +#[test] +fn test_shift_right_2() { + let original: Vec = vec![0xF6, 0x38, 0x6D]; + let expected: Vec = vec![0x07, 0xB1, 0xC3, 0x68]; + let shifted_bytes = common::shift_right(&original.clone(), 5); + + assert_array_eq(expected, shifted_bytes); +} + +#[test] +fn test_shift_right_3() { + let original: Vec = vec![0xE6, 0x38, 0x6D, 0x84]; + let expected: Vec = vec![0x0E, 0x63, 0x86, 0xD8, 0x40]; + + assert_array_eq(expected.clone(), common::shift_right(&original.clone(), 12)); + assert_array_eq(expected.clone(), common::shift_left(&original.clone(), -12)); +} + +#[test] +fn test_shift_left_0() { + let original: Vec = vec![0x53, 0x7D]; + let shifted_bytes = common::shift_left(&original.clone(), 0); + + assert_array_eq(original, shifted_bytes); +} + +#[test] +fn test_shift_left_1() { + let original: Vec = vec![0x53, 0x7D]; + let expected: Vec = vec![0x9B, 0xE8]; + + assert_array_eq(expected.clone(), common::shift_left(&original.clone(), 3)); + assert_array_eq( + expected.clone(), + common::shift_right(&original.clone(), -11), + ); +} + +#[test] +fn test_shift_left_2() { + let original: Vec = vec![0xE6, 0x38, 0x87]; + let expected: Vec = vec![0x1C, 0x43, 0x80]; + let shifted_bytes = common::shift_left(&original.clone(), 7); + + assert_array_eq(expected, shifted_bytes); +} + +#[test] +fn test_shift_left_3() { + let original: Vec = vec![0xD4, 0x4B, 0x93, 0x93]; + let expected: Vec = vec![0x89, 0x72, 0x72, 0x60]; + let shifted_bytes = common::shift_left(&original.clone(), 5); + + assert_array_eq(expected, shifted_bytes); +} + +#[test] +fn test_shift_left_4() { + let original: Vec = vec![0xE6, 0x38, 0x87, 0x0f]; + let expected: Vec = vec![0x63, 0x88, 0x70, 0xf0]; + let shifted_bytes = common::shift_left(&original.clone(), 4); + + assert_array_eq(expected, shifted_bytes); +} + +#[test] +fn test_append_bits_1() { + let byte_array_1: Vec = vec![0xE6, 0x38, 0x87]; // 1110 0110 | 0011 1000 | 1000 0111 + let byte_array_2: Vec = vec![0x6E, 0x7f]; // 0110 1110 | 0111 1111 + let mut begin: Vec = vec![]; + + assert_eq!(begin.len(), 0, "Binary buffer length should be 0 bits"); + + begin = concatinate_bits(&begin, 0, &byte_array_1, 22); // 1110 0110 | 0011 1000 | 1000 01(00) + assert_eq!(begin.len(), 3); + + assert_eq!( + "e63884", + to_hex(&begin), + "Byte array 1 should have been correctly added to the binary buffer" + ); + + begin = concatinate_bits(&begin, 22, &byte_array_2, 13); // 1110 0110 | 0011 1000 | 1000 0101 | 1011 1001 | 1110 0000 + assert_eq!(begin.len(), 5); + assert_eq!( + "e63885b9e0", + to_hex(&begin), + "Byte array 2 should have been correctly added to the binary buffer" + ); +} + +#[test] +fn removes_invalid_hex_characters() { + let input = common::remove_all_invalid_hex_characters("-i-... HELLO::am::WORLD +val+:.id"); + assert_eq!(input, "i am valid"); +} + +#[test] +fn remove_hexadecimal_prefix() { + let input_1 = common::remove_hexadecimal_prefix("0xf2sa0xasd"); + let input_2 = common::remove_hexadecimal_prefix("f2sa0xasd"); + assert_eq!(input_1, "f2sa0xasd"); + assert_eq!(input_2, "f2sa0xasd"); +} diff --git a/src/whiteflag/wf_core/basic_message.rs b/src/whiteflag/wf_core/basic_message.rs new file mode 100644 index 0000000..fcbe6ab --- /dev/null +++ b/src/whiteflag/wf_core/basic_message.rs @@ -0,0 +1,58 @@ +use super::wf_codec::common::append_bits; +use super::{segment::MessageSegment, types::MessageType}; + +pub struct BasicMessage { + message_type: MessageType, + header: MessageSegment, + body: MessageSegment, +} + +impl BasicMessage { + pub fn new( + message_type: MessageType, + header: MessageSegment, + body: MessageSegment, + ) -> BasicMessage { + BasicMessage { + message_type, + header, + body, + } + } + + pub fn encode(&self) -> (Vec, usize) { + let mut buffer: Vec = vec![]; + let mut len = 0; + + let (header_buffer, header_len) = self.header.encode(); + let (body_buffer, body_len) = self.body.encode(); + + (buffer, len) = append_bits(&buffer, len, &header_buffer, header_len); + (buffer, len) = append_bits(&buffer, len, &body_buffer, body_len); + + (buffer, len) + } + + /** + * Gets the value of the specified field + * @param fieldname the name of the requested field + * @return the field value, or NULL if field does not exist + */ + pub fn get>(&self, fieldname: T) -> String { + self.get_option(fieldname) + .expect("no value found") + .to_string() + } + + /** + * Gets the value of the specified field + * @param fieldname the name of the requested field + * @return the field value, or NULL if field does not exist + */ + pub fn get_option>(&self, fieldname: T) -> Option<&String> { + self.header + .get(fieldname.as_ref()) + .or(self.body.get(fieldname.as_ref())) + .or(None) + } +} diff --git a/src/whiteflag/wf_core/creator.rs b/src/whiteflag/wf_core/creator.rs new file mode 100644 index 0000000..ae5fd90 --- /dev/null +++ b/src/whiteflag/wf_core/creator.rs @@ -0,0 +1,105 @@ +use super::basic_message::BasicMessage; +use super::segment::MessageSegment; +use super::types::MessageType; +use super::wf_codec::common::{crop_bits, decode_from_hexadecimal, to_hex}; + +pub const PREFIX: &str = "WF"; +pub const PROTOCOL_VERSION: &str = "1"; +pub const FIELD_PREFIX: &str = "Prefix"; +pub const FIELD_VERSION: &str = "Version"; +pub const FIELD_MESSAGETYPE: &str = "MessageCode"; +pub const FIELD_TESTMESSAGETYPE: &str = "PseudoMessageCode"; + +pub fn compile + Into>(data: &[T]) -> BasicMessage { + let mut header: MessageSegment = MessageSegment::generic_header_segment(); + header.set_all(data.as_ref(), 0); + + let message_type = get_message_type(&header); + let body_start_index = header.get_number_of_fields(); + let mut body = message_type.body.clone(); + + //need switch statement here + + body.set_all(data.as_ref(), body_start_index); + BasicMessage::new(message_type, header, body) +} + +pub fn encode + Into>(fields: &[T]) -> String { + let basic_message = compile(fields); + let (message_encoded, len) = basic_message.encode(); + + to_hex(&crop_bits(message_encoded, len as isize)) +} + +/** + * Decodes an encoded Whiteflag message and creates a new Whiteflag base message object + * @since 1.1 + * @param msgBuffer a buffer with the compressed binary encoded message + * @return this message creator + * @throws WfCoreException if the encoded message is invalid + */ +pub fn decode>(message: T) -> BasicMessage { + let (buffer, bit_length) = decode_from_hexadecimal(message); + //let buffer = from_hex(message); + //let bit_length = buffer.len() * BYTE; + + let mut bit_cursor = 0; + //let mut next_field = 0; + + let mut header: MessageSegment = MessageSegment::generic_header_segment(); + bit_cursor += header.decode(&buffer, bit_length, bit_cursor, 0); // header.bit_length(); + + let message_type = get_message_type(&header); + + let mut body = message_type.body.clone(); + body.decode(&buffer, bit_length, bit_cursor, 0); + //bit_cursor += header.bit_length(); + //next_field = body.fields.len(); + + //body.decode(&buffer, bit_length, bit_cursor, next_field); + + BasicMessage::new(message_type, header, body) +} + +/* public final WfMessageCreator decode(final WfBinaryBuffer msgBuffer) throws WfCoreException { + /* Keep track of fields and bit position */ + int bitCursor = 0; + int nextField = 0; + + /* Decode message header, and determine message type */ + header = new WfMessageSegment(messageType.getHeaderFields()); + header.decode(msgBuffer, bitCursor, nextField); + bitCursor += header.bitLength(); + messageType = WfMessageType.fromCode(header.get(FIELD_MESSAGETYPE)); + + /* Decode message body and add fields as required for certain message types */ + body = new WfMessageSegment(messageType.getBodyFields()); + body.decode(msgBuffer, bitCursor, nextField); + nextField = body.getNoFields(); + bitCursor += body.bitLength(); + switch (messageType) { + case T: + /* Determine pseudo message type and extend test message body with pseudo message body */ + final WfMessageType pseudoMessageType = WfMessageType.fromCode(body.get(FIELD_TESTMESSAGETYPE)); + body.append(new WfMessageSegment(pseudoMessageType.getBodyFields())); + break; + case Q: + /* Extend request message body with request fields (calculated from remaining bits) */ + final int nRequestObjects = (msgBuffer.bitLength() - bitCursor) / 16; // One request object requires 2 fields of 8 bits + body.append(new WfMessageSegment(messageType.createRequestFields(nRequestObjects))); + break; + default: + break; + } + body.decode(msgBuffer, bitCursor, nextField); + return this; +} */ + +fn get_message_type(header: &MessageSegment) -> MessageType { + let message_code = match header.get(&FIELD_MESSAGETYPE) { + Some(x) => x.chars().next(), + _ => None, + }; + + MessageType::from_code_option(message_code.as_ref()) +} diff --git a/src/whiteflag/wf_core/definitions.rs b/src/whiteflag/wf_core/definitions.rs new file mode 100644 index 0000000..c76b3fb --- /dev/null +++ b/src/whiteflag/wf_core/definitions.rs @@ -0,0 +1,223 @@ +use super::field::Field; +use super::wf_codec::encoding::*; +use regex::Regex; + +pub enum FieldKind { + GENERIC, + AUTHENTICATION, + CRYPTO, + TEXT, + RESOURCE, + TEST, + SIGNAL, + REQUEST, +} + +pub fn message_code() -> Field { + Field::new( + "MessageCode", + Regex::new("^[A-Z]{1}$").ok(), //"(?=A|K|T|P|E|S|D|I|M|Q|R|F)^[A-Z]{1}$" + UTF8, + 5, + 6, + ) +} + +pub fn generic_header_fields() -> [Field; 7] { + [ + Field::new("Prefix", Regex::new("^WF$").ok(), UTF8, 0, 2), + Field::new("Version", Regex::new("^[A-Z0-9]{1}$").ok(), UTF8, 2, 3), //"(?=1)^[A-Z0-9]{1}$" + Field::new( + "EncryptionIndicator", + Regex::new("^[A-Z0-9]{1}$").ok(), //"(?=0|1|2)^[A-Z0-9]{1}$" + UTF8, + 3, + 4, + ), + Field::new("DuressIndicator", Regex::new("^[0-1]{1}$").ok(), BIN, 4, 5), + message_code(), + Field::new( + "ReferenceIndicator", + Regex::new( + ["^", HEX.charset, "{1}$"] //"(?=0|1|2|3|4|5|6|7|8|9)^", HEX.charset, "{1}$" + .concat() + .as_str(), + ) + .ok(), + HEX, + 6, + 7, + ), + Field::new( + "ReferencedMessage", + Regex::new(["^", HEX.charset, "{64}$"].concat().as_str()).ok(), + HEX, + 7, + 71, + ), + ] +} + +pub fn authentication_body_fields() -> [Field; 2] { + [ + Field::new( + "VerificationMethod", + Regex::new(["(?=1|2)^", HEX.charset, "{1}$"].concat().as_str()).ok(), + HEX, + 71, + 72, + ), + Field::new( + "VerificationData", + Regex::new(["^", UTF8.charset, "*$"].concat().as_str()).ok(), + UTF8, + 72, + -1, + ), + ] +} + +pub fn crypto_body_fields() -> [Field; 2] { + [ + Field::new( + "CryptoDataType", + Regex::new(["^", HEX.charset, "{2}$"].concat().as_str()).ok(), + HEX, + 71, + 73, + ), + Field::new( + "CryptoData", + Regex::new(["^", HEX.charset, "*$"].concat().as_str()).ok(), + HEX, + 73, + -1, + ), + ] +} + +pub fn freetext_body_fields() -> [Field; 1] { + [Field::new( + "Text", + Regex::new(["^", UTF8.charset, "*$"].concat().as_str()).ok(), + UTF8, + 71, + -1, + )] +} + +pub fn resource_body_fields() -> [Field; 2] { + [ + Field::new( + "ResourceMethod", + Regex::new(["(?=1)^", HEX.charset, "{1}$"].concat().as_str()).ok(), + HEX, + 71, + 72, + ), + Field::new( + "ResourceData", + Regex::new(["^", UTF8.charset, "*$"].concat().as_str()).ok(), + UTF8, + 72, + -1, + ), + ] +} + +pub fn test_body_fields() -> [Field; 1] { + [Field::new( + "PseudoMessageCode", + Regex::new("^[A-Z]{1}$").ok(), + UTF8, + 71, + 72, + )] +} + +pub fn sign_signal_body_fields() -> [Field; 9] { + [ + Field::new( + "SubjectCode", + Regex::new(["^", HEX.charset, "{2}$"].concat().as_str()).ok(), + HEX, + 71, + 73, + ), + Field::new( + "DateTime", + Regex::new(["^", DATETIME.charset, "$"].concat().as_str()).ok(), + DATETIME, + 73, + 93, + ), + Field::new( + "Duration", + Regex::new(["^", DURATION.charset, "$"].concat().as_str()).ok(), + DURATION, + 93, + 103, + ), + Field::new( + "ObjectType", + Regex::new(["^", HEX.charset, "{2}$"].concat().as_str()).ok(), + HEX, + 103, + 105, + ), + Field::new( + "ObjectLatitude", + Regex::new(["^", LAT.charset, "$"].concat().as_str()).ok(), + LAT, + 105, + 114, + ), + Field::new( + "ObjectLongitude", + Regex::new(["^", LONG.charset, "$"].concat().as_str()).ok(), + LONG, + 114, + 124, + ), + Field::new( + "ObjectSizeDim1", + Regex::new(["^", DEC.charset, "{4}$"].concat().as_str()).ok(), + DEC, + 124, + 128, + ), + Field::new( + "ObjectSizeDim2", + Regex::new(["^", DEC.charset, "{4}$"].concat().as_str()).ok(), + DEC, + 128, + 132, + ), + Field::new( + "ObjectOrientation", + Regex::new(["^", DEC.charset, "{3}$"].concat().as_str()).ok(), + DEC, + 132, + 135, + ), + ] +} + +pub fn request_fields() -> [Field; 2] { + [ + Field::new( + "ObjectType", + Regex::new(["^", HEX.charset, "{2}$"].concat().as_str()).ok(), + HEX, + 135, + 137, + ), + Field::new( + "ObjectTypeQuant", + Regex::new(["^", DEC.charset, "{2}$"].concat().as_str()).ok(), + DEC, + 137, + 139, + ), + ] +} diff --git a/src/whiteflag/wf_core/error.rs b/src/whiteflag/wf_core/error.rs new file mode 100644 index 0000000..1eae62b --- /dev/null +++ b/src/whiteflag/wf_core/error.rs @@ -0,0 +1,20 @@ +use std::fmt; + +#[derive(Debug)] +pub enum WhiteflagError { + InvalidPattern, + InvalidLength, +} + +impl std::error::Error for WhiteflagError {} + +impl fmt::Display for WhiteflagError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + WhiteflagError::InvalidPattern => write!(f, "pattern was invalid"), + WhiteflagError::InvalidLength => write!(f, "length was too short"), + } + } +} + +pub type WhiteflagResult = Result; diff --git a/src/whiteflag/wf_core/field.rs b/src/whiteflag/wf_core/field.rs new file mode 100644 index 0000000..b812abb --- /dev/null +++ b/src/whiteflag/wf_core/field.rs @@ -0,0 +1,165 @@ +use crate::wf_codec::common::extract_bits; + +use super::{ + error::{WhiteflagError, WhiteflagResult}, + wf_codec::encoding::*, +}; +use regex::Regex; + +#[derive(Clone)] +pub struct Field { + pub name: String, + pattern: Option, + encoding: Encoding, + pub start_byte: usize, + pub end_byte: isize, + value: Option, +} + +impl Field { + pub fn new( + name: &str, + pattern: Option, + encoding: Encoding, + start_byte: usize, + end_byte: isize, + ) -> Field { + Field { + name: String::from(name), + pattern, //: pattern.expect(&format!("invalid regular expression pattern: {}", name)), + encoding, + start_byte, + end_byte, + value: None, + } + } + + pub fn get_minimum_starting_position(&self) -> usize { + if self.end_byte < 0 { + return self.start_byte; + } + + self.end_byte as usize + } + + /* pub fn get(&self, data: Vec) -> WhiteflagResult { + if data.len() < self.get_minimum_starting_position() { + return Err(WhiteflagError::InvalidLength); + } + + data[self.start_byte..self.end_byte as usize] + .first() + .ok_or(WhiteflagError::InvalidLength) + } */ + + /** + * Sets the value of the message field if not already set + * @param data the data representing the field value + * @return TRUE if field value is set, FALSE if field already set or data is invalid + */ + pub fn set + Into>(&mut self, data: T) -> WhiteflagResult<()> { + if !self.is_valid(&Some(data.as_ref())) { + return Err(WhiteflagError::InvalidPattern); + } + + self.value = Some(data.into()); + Ok(()) + } + + pub fn get(&self) -> &Option { + &self.value + } + + /** + * Checks if the message field value has been set. FieldDefinition is considered set if it contains a valid value. + * @return TRUE if the field has been set, else FALSE + */ + pub fn is_set(&self) -> bool { + self.is_valid(&self.value) + } + + /** + * Checks if the message field contains a valid value + * @return TRUE if the field contains a valid value, else FALSE + */ + pub fn is_valid>(&self, data: &Option) -> bool { + let value = match data { + Some(x) => x, + None => return false, + }; + + match self.pattern.as_ref() { + Some(re) => re.is_match(value.as_ref()), + None => true, + } + } + + pub fn encode(&self) -> Option> { + match &self.value { + Some(x) => Some(self.encoding.encode(x)), + None => None, + } + } + + pub fn decode(&mut self, data: Vec) -> String { + let s = self.encoding.decode(data, self.bit_length()); + self.value = Some(s.clone()); + s + } + + /** + * Gets the byte length of the unencoded field value + * @return the byte length of the unencoded field value + */ + pub fn byte_length(&self) -> usize { + if self.end_byte < 0 { + if let Some(v) = &self.value { + return v.len(); + } else { + return 0; + } + } + + return self.end_byte as usize - self.start_byte; + } + + /** + * Gets the bit length of the encoded field + * @return the bit length of the compressed encoded field value + */ + pub fn bit_length(&self) -> usize { + return self.encoding.bit_length(self.byte_length()); + } + + /** + * Extracts and decodes a Whiteflag message field from the binary buffer + * @param field the message field to be extracted and decoded + * @param startBit the bit where the encoded field is located in the buffer + * @return String with the decoded field value + * @throws WfCoreException if field connot be decoded + */ + pub fn extract_message_field( + &mut self, + message_buffer: &[u8], + message_buffer_bit_length: usize, + start_bit: usize, + ) -> usize { + let bit_length = if self.bit_length() >= 1 { + self.bit_length() + } else { + let mut bit_length = message_buffer_bit_length - start_bit; + bit_length -= bit_length % &self.encoding.bit_length; + bit_length + }; + + let field_buffer: Vec = extract_bits( + message_buffer, + message_buffer_bit_length, + start_bit, + bit_length, + ); + self.decode(field_buffer); + + bit_length + } +} diff --git a/src/whiteflag/wf_core/field_tests.rs b/src/whiteflag/wf_core/field_tests.rs new file mode 100644 index 0000000..5fa6e9b --- /dev/null +++ b/src/whiteflag/wf_core/field_tests.rs @@ -0,0 +1,405 @@ +use super::{ + field::Field, + wf_codec::{ + common::{decode_from_hexadecimal, to_hex}, + encoding::*, + }, +}; + +const FIELDNAME: &str = "TESTFIELD"; + +#[test] +fn utf_encoding() { + let mut field = Field::new(FIELDNAME, None, UTF8, 0, -1); + field.set("WF").unwrap(); + + assert_eq!( + "5746", + to_hex(&field.encode().expect("tried encoding empty field")), + "UTF-8 field should be correctly hexadecimal encoded" + ); + assert_eq!( + 2, + field.byte_length(), + "Unencoded UTF-8 field should be 2 bytes" + ); + assert_eq!( + 16, + field.bit_length(), + "Encoded UTF-8 field should be 16 bits bytes" + ); +} + +#[test] +fn utf_decoding() { + let mut field = Field::new(FIELDNAME, None, UTF8, 0, -1); + let (buffer, _) = decode_from_hexadecimal("5746"); + let result = "WF"; + + assert_eq!( + result, + field.decode(buffer), + "UTF-8 field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "UTF-8 decoded field value should be correctly set" + ); +} + +#[test] +fn bin_encoding_1() { + let mut field = Field::new(FIELDNAME, None, BIN, 0, 8); + field.set("10111011").unwrap(); + + assert_eq!( + "bb", + to_hex(&field.encode().expect("tried encoding empty field")), + "Binary field should be correctly binary encoded" + ); + assert_eq!( + 8, + field.byte_length(), + "Unencoded Binary field should be 8 bytes" + ); + assert_eq!( + 8, + field.bit_length(), + "Encoded Binary field should be 8 bits" + ); +} + +#[test] +fn bin_decoding_1() { + let mut field = Field::new(FIELDNAME, None, BIN, 1, 7); + let (buffer, _) = decode_from_hexadecimal("aa"); + let result = "101010"; + + assert_eq!( + result, + field.decode(buffer), + "Binary field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Binary decoded field value should be correctly set" + ); +} + +#[test] +fn bin_encoding_2() { + let mut field = Field::new(FIELDNAME, None, BIN, 4, 5); + field.set("1").unwrap(); + + assert_eq!( + "80", + to_hex(&field.encode().expect("tried encoding empty field")), + "Binary field should be correctly binary encoded" + ); + assert_eq!( + 1, + field.byte_length(), + "Unencoded Binary field should be 1 bytes" + ); + assert_eq!( + 1, + field.bit_length(), + "Encoded Binary field should be 1 bits" + ); +} + +#[test] +fn bin_decoding_2_a() { + let mut field = Field::new(FIELDNAME, None, BIN, 4, 5); + let (buffer, _) = decode_from_hexadecimal("80"); + let result = "1"; + + assert_eq!( + result, + field.decode(buffer), + "Binary field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Binary decoded field value should be correctly set" + ); +} + +#[test] +fn bin_decoding_2_b() { + let mut field = Field::new(FIELDNAME, None, BIN, 2, 3); + let (buffer, _) = decode_from_hexadecimal("7f"); + let result = "0"; + + assert_eq!( + result, + field.decode(buffer), + "Binary field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Binary decoded field value should be correctly set" + ); +} + +#[test] +fn dec_encoding() { + let mut field = Field::new(FIELDNAME, None, DEC, 0, 3); + field.set("1230").unwrap(); + + assert_eq!( + "1230", + to_hex(&field.encode().expect("tried encoding empty field")), + "Decimal field should be correctly binary encoded" + ); + assert_eq!( + 3, + field.byte_length(), + "Unencoded Decimal field should be 3 bytes" + ); + assert_eq!( + 12, + field.bit_length(), + "Encoded Decimal field should be 12 bits" + ); +} + +#[test] +fn dec_decoding() { + let mut field = Field::new(FIELDNAME, None, DEC, 0, 3); + let (buffer, _) = decode_from_hexadecimal("1234"); + let result = "123"; + + assert_eq!( + result, + field.decode(buffer), + "Decimal field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Decimal decoded field value should be correctly set" + ); +} + +#[test] +fn hex_encoding() { + let mut field = Field::new(FIELDNAME, None, HEX, 0, 2); + field.set("3f").unwrap(); + + assert_eq!( + "3f", + to_hex(&field.encode().expect("tried encoding empty field")), + "Hexadecimal field should be correctly binary encoded" + ); + assert_eq!( + 2, + field.byte_length(), + "Unencoded Hexadecimal field should be 2 bytes" + ); + assert_eq!( + 8, + field.bit_length(), + "Encoded Hexadecimal field should be 8 bits" + ); +} + +#[test] +fn hex_decoding() { + let mut field = Field::new(FIELDNAME, None, HEX, 0, 2); + let (buffer, _) = decode_from_hexadecimal("0x3f"); + let result = "3f"; + + assert_eq!( + result, + field.decode(buffer), + "Hexadecimal field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Hexadecimal decoded field value should be correctly set" + ); +} + +#[test] +fn datetime_encoding() { + let mut field = Field::new(FIELDNAME, None, DATETIME, 0, -1); + field.set("2020-07-01T21:42:23Z").unwrap(); + + assert_eq!( + "20200701214223", + to_hex(&field.encode().expect("tried encoding empty field")), + "DateTime field should be correctly binary encoded" + ); + assert_eq!( + 20, + field.byte_length(), + "Unencoded DateTime field should be 20 bytes" + ); + assert_eq!( + 56, + field.bit_length(), + "Encoded DateTime field should be 56 bits" + ); +} + +#[test] +fn datetime_decoding() { + let mut field = Field::new(FIELDNAME, None, DATETIME, 0, -1); + let (buffer, _) = decode_from_hexadecimal("20200701214223"); + let result = "2020-07-01T21:42:23Z"; + + assert_eq!( + result, + field.decode(buffer), + "DateTime field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "DateTime decoded field value should be correctly set" + ); +} + +#[test] +fn duration_encoding() { + let mut field = Field::new(FIELDNAME, None, DURATION, 0, 10); + field.set("P24D11H30M").unwrap(); + + assert_eq!( + "241130", + to_hex(&field.encode().expect("tried encoding empty field")), + "Duration field should be correctly binary encoded" + ); + assert_eq!( + 10, + field.byte_length(), + "Unencoded Duration field should be 10 bytes" + ); + assert_eq!( + 24, + field.bit_length(), + "Encoded Duration field should be 24 bits" + ); +} + +#[test] +fn duration_decoding() { + let mut field = Field::new(FIELDNAME, None, DURATION, 0, 10); + let (buffer, _) = decode_from_hexadecimal("241130"); + let result = "P24D11H30M"; + + assert_eq!( + result, + field.decode(buffer), + "Duration field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Duration decoded field value should be correctly set" + ); +} + +#[test] +fn latitude_encoding() { + let mut field = Field::new(FIELDNAME, None, LAT, 0, 9); + field.set("+23.34244").unwrap(); // 1001 0001 1001 1010 0001 0010 0010 0000 + + assert_eq!( + "919a1220", + to_hex(&field.encode().expect("tried encoding empty field")), + "Latitude field should be correctly binary encoded" + ); + assert_eq!( + 9, + field.byte_length(), + "Unencoded Latitude field should be 9 bytes" + ); + assert_eq!( + 29, + field.bit_length(), + "Encoded Latitude field should be 29 bits" + ); +} + +#[test] +fn latitude_decoding() { + let mut field = Field::new(FIELDNAME, None, LAT, 0, 9); + let (buffer, _) = decode_from_hexadecimal("919a1220"); + let result = "+23.34244"; + + assert_eq!( + result, + field.decode(buffer), + "Latitude field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Latitude decoded field value should be correctly set" + ); +} + +#[test] +fn longitude_encoding() { + let mut field = Field::new(FIELDNAME, None, LONG, 0, 10); + field.set("-163.34245").unwrap(); // 0000 1011 0001 1001 1010 0001 0010 0010 1000 + + assert_eq!( + "0b19a12280", + to_hex(&field.encode().expect("tried encoding empty field")), + "Longitude field should be correctly binary encoded" + ); + assert_eq!( + 10, + field.byte_length(), + "Unencoded Longitude field should be 10 bytes" + ); + assert_eq!( + 33, + field.bit_length(), + "Encoded Longitude field should be 33 bits" + ); +} + +#[test] +fn longitude_decoding_1() { + let mut field = Field::new(FIELDNAME, None, LONG, 0, 10); + let (buffer, _) = decode_from_hexadecimal("8b19a12380"); + let result = "+163.34247"; + + assert_eq!( + result, + field.decode(buffer), + "Longitude field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Longitude decoded field value should be correctly set" + ); +} + +#[test] +fn longitude_decoding_2() { + let mut field = Field::new(FIELDNAME, None, LONG, 0, 10); + let (buffer, _) = decode_from_hexadecimal("0319a12380"); + let result = "-063.34247"; + + assert_eq!( + result, + field.decode(buffer), + "Longitude field should be correctly decoded" + ); + assert_eq!( + result, + field.get().as_ref().expect("no value was set"), + "Longitude decoded field value should be correctly set" + ); +} diff --git a/src/whiteflag/wf_core/message.rs b/src/whiteflag/wf_core/message.rs new file mode 100644 index 0000000..fcec207 --- /dev/null +++ b/src/whiteflag/wf_core/message.rs @@ -0,0 +1,242 @@ +#[derive(Clone)] +pub struct WhiteflagMessage { + pub prefix: String, + pub message_type: String, + pub version: String, + pub message_code: String, + + pub duress_indictor: String, + pub encryption_indicator: String, + pub object_type: String, + pub subject_code: String, + + pub reference_indicator: String, + pub referenced_message: String, + + pub crypto_data_type: String, + pub crypto_data: String, + + pub transaction_hash: String, + pub originator_address: String, + + pub verification_method: String, + pub verification_data: String, +} + +impl WhiteflagMessage { + pub fn new(message_code: String) -> WhiteflagMessage { + WhiteflagMessage { + prefix: "WF".to_string(), + version: "1".to_string(), + message_code: message_code.clone(), + message_type: message_code.clone(), + duress_indictor: "".to_string(), + encryption_indicator: "".to_string(), + object_type: "".to_string(), + subject_code: "".to_string(), + reference_indicator: "".to_string(), + referenced_message: "".to_string(), + crypto_data_type: "".to_string(), + crypto_data: "".to_string(), + transaction_hash: "".to_string(), + originator_address: "".to_string(), + verification_method: "".to_string(), + verification_data: "".to_string(), + } + } + + pub fn is_valid(&self) -> bool { + self.message_code == self.message_type && self.prefix != "" && self.version == "1" + } + + pub fn compile_auth_message(field_values: Vec<&str>) -> Option { + let message = WhiteflagMessage { + prefix: field_values[0].to_string(), + version: field_values[1].to_string(), + message_code: field_values[4].to_string(), + message_type: "A".to_string(), + duress_indictor: field_values[3].to_string(), + encryption_indicator: field_values[2].to_string(), + object_type: "".to_string(), + subject_code: "".to_string(), + reference_indicator: field_values[5].to_string(), + referenced_message: field_values[6].to_string(), + crypto_data_type: "".to_string(), + crypto_data: "".to_string(), + transaction_hash: "".to_string(), + originator_address: "".to_string(), + verification_method: field_values[7].to_string(), + verification_data: field_values[8].to_string(), + }; + if message.is_valid() { + return Some(message); + } else { + return None; + } + } + + pub fn get_encryption_indicator(&self) -> String { + self.encryption_indicator.clone() + } + pub fn set_encryption_indicator(&mut self, arg: String) -> bool { + if self.encryption_indicator == "" { + self.encryption_indicator = arg; + return true; + } + return false; + } + + pub fn get_subject_code(&self) -> String { + self.subject_code.clone() + } + pub fn set_subject_code(&mut self, arg: String) -> bool { + if self.subject_code == "" { + self.subject_code = arg; + return true; + } + return false; + } + + pub fn get_object_type(&self) -> String { + self.object_type.clone() + } + pub fn set_object_type(&mut self, arg: String) -> bool { + if self.object_type == "" { + self.object_type = arg; + return true; + } + return false; + } + + pub fn get_transaction_hash(&self) -> String { + self.transaction_hash.clone() + } + pub fn set_transaction_hash(&mut self, arg: String) -> Option { + if self.transaction_hash == "" { + self.transaction_hash = arg; + return None; + } + Some(self.transaction_hash.clone()) + } + + pub fn get_originator_address(&self) -> String { + self.originator_address.clone() + } + pub fn set_originator_address(&mut self, arg: String) -> Option { + if self.originator_address == "" { + self.originator_address = arg; + return None; + } + Some(self.originator_address.clone()) + } + + /// Set the whiteflag message's message type. + pub fn set_message_type(&mut self, message_type: String) { + self.message_type = message_type; + } + + /// Get a reference to the whiteflag message's prefix. + pub fn prefix(&self) -> &str { + self.prefix.as_ref() + } + + /// Get a reference to the whiteflag message's version. + pub fn version(&self) -> &str { + self.version.as_ref() + } + + /// Get a mutable reference to the whiteflag message's duress indictor. + pub fn duress_indictor_mut(&mut self) -> &mut String { + &mut self.duress_indictor + } + + /// Get a mutable reference to the whiteflag message's message code. + pub fn message_code_mut(&mut self) -> &mut String { + &mut self.message_code + } + + /// Get a mutable reference to the whiteflag message's reference indicator. + pub fn reference_indicator_mut(&mut self) -> &mut String { + &mut self.reference_indicator + } + + /// Get a mutable reference to the whiteflag message's referenced message. + pub fn referenced_message_mut(&mut self) -> &mut String { + &mut self.referenced_message + } + + /// Get a reference to the whiteflag message's crypto data type. + pub fn crypto_data_type(&self) -> &str { + self.crypto_data_type.as_ref() + } + + /// Get a mutable reference to the whiteflag message's crypto data. + pub fn crypto_data_mut(&mut self) -> &mut String { + &mut self.crypto_data + } + + /// Get a mutable reference to the whiteflag message's crypto data type. + pub fn crypto_data_type_mut(&mut self) -> &mut String { + &mut self.crypto_data_type + } + + /// Set the whiteflag message's crypto data type. + pub fn set_crypto_data_type(&mut self, crypto_data_type: String) { + self.crypto_data_type = crypto_data_type; + } + + /// Get a reference to the whiteflag message's crypto data. + pub fn crypto_data(&self) -> &str { + self.crypto_data.as_ref() + } + + /// Set the whiteflag message's crypto data. + pub fn set_crypto_data(&mut self, crypto_data: String) { + self.crypto_data = crypto_data; + } + + /// Get a mutable reference to the whiteflag message's prefix. + pub fn prefix_mut(&mut self) -> &mut String { + &mut self.prefix + } + + /// Get a reference to the whiteflag message's message type. + pub fn message_type(&self) -> &str { + self.message_type.as_ref() + } + + /// Get a mutable reference to the whiteflag message's message type. + pub fn message_type_mut(&mut self) -> &mut String { + &mut self.message_type + } + + /// Get a reference to the whiteflag message's duress indictor. + pub fn duress_indictor(&self) -> &str { + self.duress_indictor.as_ref() + } + + /// Get a reference to the whiteflag message's message code. + pub fn message_code(&self) -> &str { + self.message_code.as_ref() + } + + /// Get a reference to the whiteflag message's reference indicator. + pub fn reference_indicator(&self) -> &str { + self.reference_indicator.as_ref() + } + + /// Get a reference to the whiteflag message's referenced message. + pub fn referenced_message(&self) -> &str { + self.referenced_message.as_ref() + } + + /// Get a reference to the whiteflag message's verification method. + pub fn verification_method(&self) -> &str { + self.verification_method.as_ref() + } + + /// Get a reference to the whiteflag message's verification data. + pub fn verification_data(&self) -> &str { + self.verification_data.as_ref() + } +} diff --git a/src/whiteflag/wf_core/message_tests.rs b/src/whiteflag/wf_core/message_tests.rs new file mode 100644 index 0000000..1da5c95 --- /dev/null +++ b/src/whiteflag/wf_core/message_tests.rs @@ -0,0 +1,228 @@ +use super::creator::{decode, encode}; + +#[test] +fn encode_sign_signal_message() { + let encoding_result: String = "57463130a6a1f7da7067d41891592131a12a60c9053b4eb0aefe6263385da9f5b789421e1d7401009841882148a800000114c1e596006f04c050eca6420084".to_string(); + let field_values = vec![ + "WF", + "1", + "0", + "1", + "M", + "4", + "3efb4e0cfa83122b242634254c1920a769d615dfcc4c670bb53eb6f12843c3ae", + "80", + "2013-08-31T04:29:15Z", + "P00D00H00M", + "22", + "+30.79658", + "-037.82602", + "8765", + "3210", + "042", + ]; + + assert_eq!( + encoding_result, + encode(&field_values), + "Encoding should be correct" + ); +} + +#[test] +fn decode_sign_signal_message() { + let encoding_result: String = "57463130a6a1f7da7067d41891592131a12a60c9053b4eb0aefe6263385da9f5b789421e1d7401009841882148a800000114c1e596006f04c050eca6420084".to_string(); + let field_values = vec![ + "WF", + "1", + "0", + "1", + "M", + "4", + "3efb4e0cfa83122b242634254c1920a769d615dfcc4c670bb53eb6f12843c3ae", + "80", + "2013-08-31T04:29:15Z", + "P00D00H00M", + "22", + "+30.79658", + "-037.82602", + "8765", + "3210", + "042", + ]; + + let message = decode(encoding_result); + + //assert_eq!(message.set("Version", "2"), "Should not be able to change version field"); + assert_eq!( + field_values[0], + message.get("Prefix"), + "Prefix should be correctly set" + ); + assert_eq!( + field_values[1], + message.get("Version"), + "Version number should be correctly set" + ); + assert_eq!( + field_values[2], + message.get("EncryptionIndicator"), + "Encryption indicator should be correctly set" + ); + assert_eq!( + field_values[3], + message.get("DuressIndicator"), + "Duress indicator should be correctly set" + ); + assert_eq!( + field_values[4], + message.get("MessageCode"), + "Message code should be correctly set" + ); + assert_eq!( + field_values[5], + message.get("ReferenceIndicator"), + "Reference indicator should be correctly set" + ); + assert_eq!( + field_values[6], + message.get("ReferencedMessage"), + "Referenced message should be correctly set" + ); + assert_eq!( + field_values[7], + message.get("SubjectCode"), + "Subject code should be correctly set" + ); + assert_eq!( + field_values[8], + message.get("DateTime"), + "DateTime should be correctly set" + ); + assert_eq!( + field_values[9], + message.get("Duration"), + "Duration should be correctly set" + ); + assert_eq!( + field_values[10], + message.get("ObjectType"), + "Object code should be correctly set" + ); + assert_eq!( + field_values[11], + message.get("ObjectLatitude"), + "Latitude should be correctly set" + ); + assert_eq!( + field_values[12], + message.get("ObjectLongitude"), + "Longitude should be correctly set" + ); + assert_eq!( + field_values[13], + message.get("ObjectSizeDim1"), + "Size dimention 1 should be correctly set" + ); + assert_eq!( + field_values[14], + message.get("ObjectSizeDim2"), + "Size dimention 2 should be correctly set" + ); + assert_eq!( + field_values[15], + message.get("ObjectOrientation"), + "Orientation should be correctly set" + ); +} + +#[test] +fn encode_auth_message() { + let encoding_result: String = "5746313020800000000000000000000000000000000000000000000000000000000000000000b43a3a38399d1797b7b933b0b734b9b0ba34b7b71734b73a17bbb434ba32b33630b380".to_string(); + let field_values = vec![ + "WF", + "1", + "0", + "0", + "A", + "0", + "0000000000000000000000000000000000000000000000000000000000000000", + "1", + "https://organisation.int/whiteflag", + ]; + + assert_eq!( + encoding_result, + encode(&field_values), + "Encoding should be correct" + ); +} + +#[test] +fn decode_auth_message() { + /* Setup */ + let field_values = vec![ + "WF", + "1", + "0", + "0", + "A", + "0", + "0000000000000000000000000000000000000000000000000000000000000000", + "1", + "https://organisation.int/whiteflag", + ]; + + let message = decode("5746313020800000000000000000000000000000000000000000000000000000000000000000b43a3a38399d1797b7b933b0b734b9b0ba34b7b71734b73a17bbb434ba32b33630b380"); + + /* Verify */ + //assert_eq!("Message type should be correct", A, message.getType()); + //assert_eq!(fieldValues.length, message.getNoFields(), "Number of fields should be equal to number of provided fields"); + //assert_eq!(message.getFieldNames().size(), message.getNoFields(), "Number of fields should be equal to number of field names in set"); + assert_eq!( + field_values[0], + message.get("Prefix"), + "Prefix should be correctly set" + ); + assert_eq!( + field_values[1], + message.get("Version"), + "Version number should be correctly set" + ); + assert_eq!( + field_values[2], + message.get("EncryptionIndicator"), + "Encryption indicator should be correctly set" + ); + assert_eq!( + field_values[3], + message.get("DuressIndicator"), + "Duress indicator should be correctly set" + ); + assert_eq!( + field_values[4], + message.get("MessageCode"), + "Message code should be correctly set" + ); + assert_eq!( + field_values[5], + message.get("ReferenceIndicator"), + "Reference indicator should be correctly set" + ); + assert_eq!( + field_values[6], + message.get("ReferencedMessage"), + "Referenced message should be correctly set" + ); + assert_eq!( + field_values[7], + message.get("VerificationMethod"), + "Verification method should be correctly set" + ); + assert_eq!( + field_values[8], + message.get("VerificationData"), + "Verification data should be correctly set" + ); + //assertTrue("Message should be valid", message.isValid()); +} diff --git a/src/whiteflag/wf_core/mod.rs b/src/whiteflag/wf_core/mod.rs new file mode 100644 index 0000000..5a9ea09 --- /dev/null +++ b/src/whiteflag/wf_core/mod.rs @@ -0,0 +1,16 @@ +#[cfg(test)] +mod field_tests; + +#[cfg(test)] +mod message_tests; + +use super::wf_codec; + +pub mod basic_message; +pub mod creator; +pub mod definitions; +pub mod error; +pub mod field; +pub mod message; +pub mod segment; +pub mod types; diff --git a/src/whiteflag/wf_core/segment.rs b/src/whiteflag/wf_core/segment.rs new file mode 100644 index 0000000..823a1dd --- /dev/null +++ b/src/whiteflag/wf_core/segment.rs @@ -0,0 +1,183 @@ +use super::definitions::generic_header_fields; +use super::field::Field; + +#[derive(Clone)] +pub struct MessageSegment { + pub fields: Vec, +} + +impl MessageSegment { + pub fn from(fields: Vec) -> MessageSegment { + MessageSegment { fields } + } + + pub fn generic_header_segment() -> MessageSegment { + MessageSegment::from(generic_header_fields().to_vec()) + } + + /* + * Sets all field values of this segment with values from an array + * @since 1.1 + * @param data array with the data to be set as the field values + * @param startIndex starting position in the array + * @return TRUE if the data was valid and all field values are set + * @throws WfCoreException if the provided data is invalid + */ + pub fn set_all + Into>(&mut self, data: &[T], start_index: usize) { + /* int nItems = data.length - startIndex; + if (nItems < fields.length) { + throw new WfCoreException("Message segment has " + fields.length + " fields, but received " + nItems + " items in array", null); + } */ + let mut index = start_index; + for field in &mut self.fields { + /* if (Boolean.FALSE.equals(field.set(data[index]))) { + throw new WfCoreException("Field " + field.debugInfo() + " already set or array item " + index + " contains invalid data: " + data[index], null); + } */ + let value = &data[index]; + field.set(value.as_ref()).unwrap(); + index += 1; + } + + //return this.isValid(); + } + + /** + * Gets the value of the field specified by name + * @param fieldname the name of the requested field + * @return the field value, or NULL if field does not exist + */ + pub fn get>(&self, field_name: T) -> Option<&String> { + let value = self + .fields + .iter() + .find(|f| f.name == field_name.as_ref())? + .get(); + + value.as_ref() + } + + pub fn get_number_of_fields(&self) -> usize { + self.fields.len() + } + + /** + * Encodes this message segment + * @return a binary buffer with the binary encoded message segment and its bit length + * @throws WfCoreException if the message cannot be encoded + */ + pub fn encode(&self) -> (Vec, usize) { + let mut buffer: Vec = vec![]; + let mut len = buffer.len(); + //let cursor = self.fields[0].start_byte; + for field in &self.fields { + /* if (field.startByte != byteCursor) { + throw new WfCoreException("Invalid field order while encoding: did not expect field " + field.debugInfo() + " at byte " + byteCursor, null); + } */ + let field_length = field.bit_length(); + //buffer.appendBits(field.encode(), field.bitLength()); + buffer = super::wf_codec::common::concatinate_bits( + &buffer, + len, + &field.encode().expect("field had no value"), + field_length, + ); + + len += field_length; + //byteCursor = field.endByte; + } + + (buffer, len) + } + + /** + * Decodes this message segment from the provided encoded message + * @since 1.1 + * @param buffer the binary buffer with the binary encoded message + * @param startBit the bit position where this segment starts in the encoded message + * @param fieldIndex the index of the next field to be decoded + * @throws WfCoreException if the message cannot be decoded + */ + pub fn decode( + &mut self, + message_buffer: &[u8], + message_buffer_bit_length: usize, + start_bit: usize, + field_index: usize, + ) -> usize { + /* Check if all fields already processed */ + if field_index > self.fields.len() { + return 0; + } + + let mut bit_cursor = start_bit; + let mut byte_cursor = self.fields[field_index].start_byte; + for field in &mut self.fields[field_index..] { + if field.start_byte != byte_cursor { + panic!("start byte should match byte cursor"); + //throw new WfCoreException("Invalid field order while decoding: did not expect field " + fields[index].debugInfo() + " at byte " + byteCursor, null); + } + /* + try { + buffer.extractMessageField(fields[index], bitCursor); + } catch (WfCoreException e) { + throw new WfCoreException("Could not decode field at bit " + bitCursor + " of buffer: " + buffer.toHexString(), e); + } */ + + let bit_length = + field.extract_message_field(message_buffer, message_buffer_bit_length, bit_cursor); + + bit_cursor += bit_length; //field.bit_length(); + byte_cursor = field.end_byte as usize; + } + + bit_cursor - start_bit + } + + /** + * Returns the bit length of this segment, excluding the last variable length field if not set + * @return the bit length of this segment + */ + pub fn bit_length(&self) -> usize { + self.bit_length_of_field(self.fields.len() as isize) + } + + /** + * Returns the bit length up to and including the specified field, excluding the last variable length field if not set + * @param fieldIndex the index of the field up to which the segment length is calculated; negative index counts back from last field + * @return the bit length of this segment up to and including the specified field, or 0 if the field does not exist + */ + pub fn bit_length_of_field(&self, field_index: isize) -> usize { + /* Check provided index */ + let last_field_index = self.get_absolute_index(field_index); + if last_field_index < 0 { + return 0; + } + + /* Calculate segment bit length */ + let mut bit_length = 0; + for index in 0..last_field_index as usize { + bit_length += self.fields[index].bit_length(); + } + + bit_length + } + + /** + * Gets the absolute field index and check if index is within bounds + * @param index the absolute index of the requested field; negative index counts back from last field + * @return the absolute field index or -1 if index out of bounds + */ + fn get_absolute_index(&self, index: isize) -> isize { + let length = self.fields.len() as isize; + + if index >= 0 && index < length { + return index; + } + + if index < 0 && (length + index) >= 0 { + return length + index; + } + + return -1; + } +} diff --git a/src/whiteflag/wf_core/types.rs b/src/whiteflag/wf_core/types.rs new file mode 100644 index 0000000..c02fffa --- /dev/null +++ b/src/whiteflag/wf_core/types.rs @@ -0,0 +1,132 @@ +use super::definitions::*; +use super::field::Field; +use super::segment::MessageSegment; + +pub struct MessageType { + message_code: char, + pub headers: MessageSegment, + pub body: MessageSegment, +} + +impl MessageType { + pub fn from_code_option(code: Option<&char>) -> MessageType { + let c = code.unwrap_or(&' '); + Self::from_code(c) + } + + pub fn from_code(code: &char) -> MessageType { + let body: Vec = match code { + 'A' => authentication_body_fields().to_vec(), + 'K' => crypto_body_fields().to_vec(), + 'T' => test_body_fields().to_vec(), + 'R' => resource_body_fields().to_vec(), + 'F' => freetext_body_fields().to_vec(), + 'P' | 'E' | 'D' | 'S' | 'I' | 'M' | 'Q' => sign_signal_body_fields().to_vec(), + _ => Vec::::new(), + }; + + MessageType { + message_code: *code, + headers: MessageSegment::generic_header_segment(), + body: MessageSegment::from(body), + } + } +} + +pub enum MessageTypeEnum { + /** + * Undefined message type + */ + Any, //UNDEFINED + + /** + * Authentication message type + *

Message introducing the sender on the network with the sender’s authentication data + * @wfref 4.3.4 Management Messages: Authentication + */ + Authentication, //("A", authenticationBodyFields), + + /** + * Cryptographic message type + *

Message for management of keys and parameters of cryptographic functions + * @wfref 4.3.5 Management Messages: Cryptographic Support + */ + Cryptographic, //("K", cryptoBodyFields), + + /** + * Test message type + *

Message that can be used for testing Whiteflag functionality by applications + * @wfref 4.3.6 Management Messages: Test + */ + Test, //("T", testBodyFields), + + /** + * Resource message type + *

Message to point to an internet resource + * @wfref 4.3.2 Functional Messages: Resource + */ + Resource, //("R", resourceBodyFields), + + /** + * Free Text message type + *

Message to send a free text string + * @wfref 4.3.3 Functional Messages: Free Text + */ + FreeText, //("F", freetextBodyFields), + + /** + * Protective Sign message type + *

Sign to mark objects under the protection of international law + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.1 Protective Signs + */ + Protective, //("P", signsignalBodyFields), + + /** + * Emergency Signal message type + *

Signal to send an emergency signal when in need of assistance + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.2 Emergency Signals + */ + Emergency, //("E", signsignalBodyFields), + + /** + * Danger Sign message type + *

Sign to mark a location or area of imminent danger, e.g. an area under attack, land mines, disaster, etc. + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.3 Danger and Disaster Signs + */ + Danger, //("D", signsignalBodyFields), + + /** + * Status Signal message type + *

Signal to provide the status of an object, or specifically for persons: give a proof of life + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.4 Status Signals + */ + Status, //("S", signsignalBodyFields), + + /** + * Infrastructure Sign message type + *

Sign to mark critical infrastructure, e.g. roads, utilities, water treatment, hospitals, power plants etc. + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.5 Infrastructure Signs + */ + Infrastructure, //("I", signsignalBodyFields), + + /** + * Mission Signal message type + *

Signal to provide information on activities undertaken during a mission + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.6 Mission Signals + */ + Mission, //("M", signsignalBodyFields), + + /** + * Request Signal message type + *

Signal to perform requests to other parties + * @wfref 4.3.1 Functional Messages: Signs/Signals + * @wfref 4.3.1.2.7 Request Signals + */ + Request, //Q("Q", signsignalBodyFields); +}