diff --git a/Cargo.lock b/Cargo.lock index fbe334a..3c742a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,114 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "tar-ng" version = "0.1.0" +dependencies = [ + "md-5", + "sha1", + "sha2", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/Cargo.toml b/Cargo.toml index 766285b..888c243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +md-5 = "0.10" +sha1 = "0.10" +sha2 = "0.10" diff --git a/src/lib.rs b/src/lib.rs index 10d62d2..620ad4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,14 @@ -use std::{ - array::TryFromSliceError, - fmt, - io::{self, Read, Write}, - num::TryFromIntError, - string::FromUtf8Error, +use { + md5::{Digest, Md5}, + sha1::Sha1, + sha2::Sha256, + std::{ + array::TryFromSliceError, + fmt, + io::{self, Read, Write}, + num::TryFromIntError, + string::FromUtf8Error, + }, }; #[derive(Debug)] @@ -12,6 +17,7 @@ pub enum Error { Io(io::Error), Utf8(FromUtf8Error), Slice(TryFromSliceError), + InvalidChecksum, MissingData, UnexpectedData, UnknownFileType, @@ -24,6 +30,7 @@ impl fmt::Display for Error { Self::Utf8(e) => write!(f, "{e}"), Self::Slice(e) => write!(f, "{e}"), Self::Io(e) => write!(f, "{e}"), + Self::InvalidChecksum => write!(f, "invalid checksum"), Self::MissingData => write!(f, "missing data"), Self::UnexpectedData => write!(f, "unexpected data"), Self::UnknownFileType => write!(f, "unknown file type"), @@ -91,6 +98,117 @@ impl Special { } } +#[derive(Debug)] +pub enum Checksum { + Md5([u8; 16]), + Sha1([u8; 20]), + Sha256([u8; 32]), + Skip, +} + +impl Checksum { + pub fn read(reader: &mut T) -> Result { + let mut buf = [0; 1]; + reader.read_exact(&mut buf)?; + match buf[0] { + 0 => { + let mut sum = [0; 16]; + reader.read_exact(&mut sum)?; + Ok(Self::Md5(sum)) + }, + 1 => { + let mut sum = [0; 20]; + reader.read_exact(&mut sum)?; + Ok(Self::Sha1(sum)) + }, + 2 => { + let mut sum = [0; 32]; + reader.read_exact(&mut sum)?; + Ok(Self::Sha256(sum)) + }, + _ => Ok(Self::Skip), + } + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + match self { + Self::Md5(sum) => { + writer.write_all(&[b'0'])?; + writer.write_all(&sum[..])?; + }, + Self::Sha1(sum) => { + writer.write_all(&[b'1'])?; + writer.write_all(&sum[..])?; + }, + Self::Sha256(sum) => { + writer.write_all(&[b'2'])?; + writer.write_all(&sum[..])?; + }, + Self::Skip => writer.write_all(&[b'3'])?, + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct File { + pub len: u64, + pub checksum: Checksum, + pub data: Vec, +} + +impl File { + pub fn read(reader: &mut T) -> Result { + let mut len = [0; 8]; + reader.read_exact(&mut len)?; + let len = u64::from_le_bytes(len); + let checksum = Checksum::read(reader)?; + let mut data = Vec::with_capacity(len.try_into()?); + let mut handle = reader.take(len); + handle.read_exact(&mut data)?; + match checksum { + Checksum::Md5(sum) => { + let mut hasher = Md5::new(); + hasher.update(&data); + let res = hasher.finalize(); + if res == sum.into() { + Ok(Self { len, checksum, data }) + } else { + Err(Error::InvalidChecksum) + } + }, + Checksum::Sha1(sum) => { + let mut hasher = Sha1::new(); + hasher.update(&data); + let res = hasher.finalize(); + if res == sum.into() { + Ok(Self { len, checksum, data }) + } else { + Err(Error::InvalidChecksum) + } + }, + Checksum::Sha256(sum) => { + let mut hasher = Sha256::new(); + hasher.update(&data); + let res = hasher.finalize(); + if res == sum.into() { + Ok(Self { len, checksum, data }) + } else { + Err(Error::InvalidChecksum) + } + }, + _ => Ok(Self { len, checksum, data }), + } + } + + pub fn write(&self, writer: &mut T) -> Result<(), Error> { + writer.write_all(&self.len.to_be_bytes())?; + Checksum::write(&self.checksum, writer)?; + writer.write_all(&self.data)?; + Ok(()) + } +} + #[repr(u8)] #[derive(Debug)] pub enum FileType { @@ -256,12 +374,12 @@ impl Node { } else { return Err(Error::MissingData); } - }, + } _ => { if self.data.is_some() { return Err(Error::UnexpectedData); } - }, + } } Ok(()) }