diff --git a/impls/src/adapters/slatepack.rs b/impls/src/adapters/slatepack.rs index 2aa32494..f47b3852 100644 --- a/impls/src/adapters/slatepack.rs +++ b/impls/src/adapters/slatepack.rs @@ -13,11 +13,11 @@ // limitations under the License. /// Slatepack Output 'plugin' implementation -use std::fs::File; +use std::fs::{metadata, File}; use std::io::{Read, Write}; use std::path::PathBuf; -use crate::libwallet::{Error, ErrorKind, Slate, Slatepack, SlatepackBin, Slatepacker}; +use crate::libwallet::{slatepack, Error, ErrorKind, Slate, Slatepack, SlatepackBin, Slatepacker}; use crate::{SlateGetter, SlatePutter}; use grin_wallet_util::byte_ser; @@ -39,6 +39,17 @@ impl<'a> PathToSlatepack<'a> { } pub fn get_slatepack_file_contents(&self) -> Result, Error> { + let metadata = metadata(&self.pathbuf)?; + let len = metadata.len(); + let min_len = slatepack::min_size(); + let max_len = slatepack::max_size(); + if len < min_len || len > max_len { + let msg = format!( + "Data is invalid length: {} | min: {}, max: {} |", + len, min_len, max_len + ); + return Err(ErrorKind::SlatepackDeser(msg).into()); + } let mut pub_tx_f = File::open(&self.pathbuf)?; let mut data = Vec::new(); pub_tx_f.read_to_end(&mut data)?; @@ -84,3 +95,85 @@ impl<'a> SlateGetter for PathToSlatepack<'a> { Ok((self.packer.get_slate(&slatepack)?, true)) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + use grin_wallet_util::grin_core::global; + + fn clean_output_dir(test_dir: &str) { + let _ = fs::remove_dir_all(test_dir); + } + + fn setup(test_dir: &str) { + clean_output_dir(test_dir); + } + + const SLATEPACK_DIR: &'static str = "target/test_output/slatepack"; + + #[test] + fn pathbuf_get_file_contents() { + global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + setup(SLATEPACK_DIR); + + fs::create_dir_all(SLATEPACK_DIR).unwrap(); + let sp_path = PathBuf::from(SLATEPACK_DIR).join("pack_file"); + + // set Slatepack file to minimum allowable size + { + let f = File::create(sp_path.clone()).unwrap(); + f.set_len(slatepack::min_size()).unwrap(); + } + + let args = slatepack::SlatepackerArgs { + sender: None, + recipients: vec![], + dec_key: None, + }; + let packer = Slatepacker::new(args); + + let mut pack_path = PathToSlatepack::new(sp_path.clone(), &packer, true); + assert!(pack_path.get_slatepack_file_contents().is_ok()); + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, false); + assert!(pack_path.get_slatepack_file_contents().is_ok()); + + // set Slatepack file to maximum allowable size + { + let f = File::create(sp_path.clone()).unwrap(); + f.set_len(slatepack::max_size()).unwrap(); + } + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, true); + assert!(pack_path.get_slatepack_file_contents().is_ok()); + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, false); + assert!(pack_path.get_slatepack_file_contents().is_ok()); + + // set Slatepack file below minimum allowable size + { + let f = File::create(sp_path.clone()).unwrap(); + f.set_len(slatepack::min_size() - 1).unwrap(); + } + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, true); + assert!(pack_path.get_slatepack_file_contents().is_err()); + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, false); + assert!(pack_path.get_slatepack_file_contents().is_err()); + + // set Slatepack file above maximum allowable size + { + let f = File::create(sp_path.clone()).unwrap(); + f.set_len(slatepack::max_size() + 1).unwrap(); + } + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, true); + assert!(pack_path.get_slatepack_file_contents().is_err()); + + pack_path = PathToSlatepack::new(sp_path.clone(), &packer, false); + assert!(pack_path.get_slatepack_file_contents().is_err()); + } +} diff --git a/libwallet/src/lib.rs b/libwallet/src/lib.rs index 5121c52c..a9dce0b1 100644 --- a/libwallet/src/lib.rs +++ b/libwallet/src/lib.rs @@ -52,7 +52,7 @@ mod error; mod internal; mod slate; pub mod slate_versions; -mod slatepack; +pub mod slatepack; mod types; pub use crate::error::{Error, ErrorKind}; diff --git a/libwallet/src/slatepack/armor.rs b/libwallet/src/slatepack/armor.rs index 74120c1b..7988ce86 100644 --- a/libwallet/src/slatepack/armor.rs +++ b/libwallet/src/slatepack/armor.rs @@ -21,7 +21,7 @@ // Finally add armor framing and space/newline formatting as desired use crate::{Error, ErrorKind}; -use grin_wallet_util::byte_ser; +use grin_wallet_util::{byte_ser, grin_core::global::max_tx_weight}; use regex::Regex; use sha2::{Digest, Sha256}; use std::str; @@ -33,6 +33,20 @@ pub static HEADER: &str = "BEGINSLATEPACK."; static FOOTER: &str = ". ENDSLATEPACK."; const WORD_LENGTH: usize = 15; const WORDS_PER_LINE: usize = 200; +const WEIGHT_RATIO: u64 = 32; + +/// Maximum size for an armored Slatepack file +pub fn max_size() -> u64 { + max_tx_weight() + .saturating_mul(WEIGHT_RATIO) + .saturating_add(HEADER.len() as u64) + .saturating_add(FOOTER.len() as u64) +} + +/// Minimum size for an armored Slatepack file or stream +pub fn min_size() -> u64 { + HEADER.len() as u64 +} lazy_static! { static ref HEADER_REGEX: Regex = diff --git a/libwallet/src/slatepack/mod.rs b/libwallet/src/slatepack/mod.rs index ab72b94b..c2dbcafd 100644 --- a/libwallet/src/slatepack/mod.rs +++ b/libwallet/src/slatepack/mod.rs @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Functions and types for handling Slatepack transactions + mod address; mod armor; mod packer; mod types; pub use self::address::SlatepackAddress; -pub use self::armor::SlatepackArmor; +pub use self::armor::{max_size, min_size, SlatepackArmor}; pub use self::packer::{Slatepacker, SlatepackerArgs}; pub use self::types::{Slatepack, SlatepackBin}; diff --git a/libwallet/src/slatepack/packer.rs b/libwallet/src/slatepack/packer.rs index ac7578b1..f42b4853 100644 --- a/libwallet/src/slatepack/packer.rs +++ b/libwallet/src/slatepack/packer.rs @@ -16,11 +16,11 @@ use std::convert::TryFrom; use std::str; use super::armor::HEADER; -use crate::{Error, ErrorKind}; use crate::{ - Slate, SlateVersion, Slatepack, SlatepackAddress, SlatepackArmor, SlatepackBin, + slatepack, Slate, SlateVersion, Slatepack, SlatepackAddress, SlatepackArmor, SlatepackBin, VersionedBinSlate, VersionedSlate, }; +use crate::{Error, ErrorKind}; use grin_wallet_util::byte_ser; @@ -50,8 +50,9 @@ impl<'a> Slatepacker<'a> { /// return slatepack pub fn deser_slatepack(&self, data: &[u8], decrypt: bool) -> Result { // check if data is armored, if so, remove and continue - if data.len() < super::armor::HEADER.len() { - let msg = format!("Data too short"); + let data_len = data.len() as u64; + if data_len < slatepack::min_size() || data_len > slatepack::max_size() { + let msg = format!("Data invalid length"); return Err(ErrorKind::SlatepackDeser(msg).into()); }