Skip to content

Commit

Permalink
Check Slatepack file size before loading contents
Browse files Browse the repository at this point in the history
Check Slatepack file size is within valid range before loading contents
into memory
  • Loading branch information
Nym Seddon committed Aug 10, 2020
1 parent b58322f commit 7c46c95
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 9 deletions.
97 changes: 95 additions & 2 deletions impls/src/adapters/slatepack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -39,6 +39,17 @@ impl<'a> PathToSlatepack<'a> {
}

pub fn get_slatepack_file_contents(&self) -> Result<Vec<u8>, 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)?;
Expand Down Expand Up @@ -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());
}
}
2 changes: 1 addition & 1 deletion libwallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
16 changes: 15 additions & 1 deletion libwallet/src/slatepack/armor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 =
Expand Down
4 changes: 3 additions & 1 deletion libwallet/src/slatepack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
9 changes: 5 additions & 4 deletions libwallet/src/slatepack/packer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -50,8 +50,9 @@ impl<'a> Slatepacker<'a> {
/// return slatepack
pub fn deser_slatepack(&self, data: &[u8], decrypt: bool) -> Result<Slatepack, Error> {
// 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());
}

Expand Down

0 comments on commit 7c46c95

Please sign in to comment.