]>
code.octet-stream.net Git - hashgood/blob - src/main.rs
fd965dac05a1576b4f380cf596eff66559db779d
   2 use std
::path
::{Path
, PathBuf
}; 
   4 use structopt
::StructOpt
; 
   6 /// Calculate digests for given input data 
   9 /// Display output nicely in the terminal 
  12 /// Collect candidate hashes based on options and match them against a calculated hash 
  16 #[structopt(name = "hashgood")] 
  18     /// Read the hash from the clipboard 
  19     #[cfg(feature = "paste")] 
  20     #[structopt(short = "p", long = "paste")] 
  23     /// Disable ANSI colours in output 
  24     #[structopt(short = "C", long = "no-colour")] 
  27     /// A file containing the hash to verify. It can either be a raw hash or a SHASUMS-style listing. Use `-` for standard input. 
  28     #[structopt(short = "c", long = "check", parse(from_os_str))] 
  29     hash_file
: Option
<PathBuf
>, 
  31     /// The file to be verified or `-` for standard input 
  32     #[structopt(name = "input", parse(from_os_str))] 
  35     /// A hash to verify, supplied directly on the command line 
  36     #[structopt(name = "hash")] 
  41     fn get_paste(&self) -> bool 
{ 
  42         #[cfg(feature = "paste")] 
  46         #[cfg(not(feature = "paste"))] 
  53 /// Types of supported digest algorithm 
  54 #[derive(Debug, PartialEq, Copy, Clone)] 
  62     /// Assume a hash type from the binary length. Fortunately the typical 3 algorithms we care about are different lengths. 
  63     pub fn from_len(len
: usize) -> Result
<Algorithm
, String
> { 
  65             16 => Ok(Algorithm
::Md5
), 
  66             20 => Ok(Algorithm
::Sha1
), 
  67             32 => Ok(Algorithm
::Sha256
), 
  68             _ 
=> Err(format
!("Unrecognised hash length: {} bytes", len
)), 
  73 /// The method by which one or more hashes were supplied to verify the calculated digest 
  74 #[derive(Debug, PartialEq)] 
  75 pub enum VerificationSource 
{ 
  82 /// A complete standalone hash result 
  90     pub fn new(alg
: Algorithm
, bytes
: Vec
<u8>, path
: &Path
) -> Self { 
  91         // Taking the filename component should always work? 
  92         // If not, just fall back to the full path 
  93         let filename 
= match path
.file
_name
() { 
  94             Some(filename
) => filename
.to_string_lossy(), 
  95             None 
=> path
.to_string_lossy(), 
 100             filename
: filename
.to_string(), 
 105 /// A possible hash to match against. The algorithm is assumed. 
 106 #[derive(Debug, PartialEq)] 
 107 pub struct CandidateHash 
{ 
 109     filename
: Option
<String
>, 
 112 /// A list of candidate hashes that our input could potentially match. At this point it is 
 113 /// assumed that we will be verifying a digest of a particular, single algorithm. 
 114 #[derive(Debug, PartialEq)] 
 115 pub struct CandidateHashes 
{ 
 117     hashes
: Vec
<CandidateHash
>, 
 118     source
: VerificationSource
, 
 121 /// Summary of an atetmpt to match the calculated digest against candidates 
 122 pub enum MatchLevel 
{ 
 128 /// The severity of any informational messages to be printed before the final result 
 129 pub enum MessageLevel 
{ 
 135 /// Overall details of an attempt to match the calculated digest against candidates 
 136 pub struct Verification
<'a
> { 
 137     match_level
: MatchLevel
, 
 138     comparison_hash
: Option
<&'a CandidateHash
>, 
 139     messages
: Vec
<(MessageLevel
, String
)>, 
 142 /// Entry point - run the program and handle errors ourselves cleanly. 
 144 /// At the moment there aren't really any errors that can be handled by the application. Therefore 
 145 /// stringly-typed errors are used and they are all captured here, where the problem is printed 
 146 /// and the application terminates with a non-zero return code. 
 148     hashgood().unwrap
_or
_else
(|e
| { 
 149         eprintln
!("Error: {}", e
); 
 154 /// Main application logic 
 155 fn hashgood() -> Result
<(), Box
<dyn Error
>> { 
 156     let opt 
= get_verified_options()?
; 
 157     let candidates 
= verify
::get_candidate_hashes(&opt
)?
; 
 158     let input 
= calculate
::get_input_reader(opt
.inp
ut
.as_path())?
; 
 159     if let Some(c
) = candidates 
{ 
 160         // If we have a candidate hash of a particular type, use that specific algorithm 
 161         let hashes 
= calculate
::create_digests(&[c
.alg
], input
)?
; 
 162         for (alg
, bytes
) in hashes 
{ 
 163             // Should always be true 
 165                 let hash 
= Hash
::new(alg
, bytes
, &opt
.inp
ut
); 
 166                 let verification 
= verify
::verify_hash(&hash
, &c
); 
 169                     verification
.comparison_hash
, 
 173                 display
::print_messages(verification
.messages
, opt
.no_colour
)?
; 
 174                 display
::print_match_level(verification
.match_level
, opt
.no_colour
)?
; 
 178         // If no candidate, calculate all three common digest types for output 
 179         let hashes 
= calculate
::create_digests( 
 180             &[Algorithm
::Md5
, Algorithm
::Sha1
, Algorithm
::Sha256
], 
 183         for (alg
, bytes
) in hashes 
{ 
 187                 filename
: opt
.inp
ut
.file
_name
().unwrap
().to_string_lossy().to_string(), 
 189             display
::print_hash(&hash
, None
, None
, opt
.no_colour
)?
; 
 195 /// Parse the command line options and check for ambiguous or inconsistent settings 
 196 fn get_verified_options() -> Result
<Opt
, String
> { 
 197     let opt 
= Opt
::from_args(); 
 199         opt
.hash
.is
_some
() as i32 + opt
.get_paste() as i32 + opt
.hash_file
.is
_some
() as i32; 
 200     if hash_methods 
> 1 { 
 201         if opt
.hash
.is
_some
() { 
 202             eprintln
!("* specified as command line argument"); 
 205             eprintln
!("* paste from clipboard (-p)") 
 207         if opt
.hash_file
.is
_some
() { 
 208             eprintln
!("* check hash from file (-c)") 
 210         return Err("Error: Hashes were provided by multiple methods. Use only one.".to_owned()); 
 212     if opt
.inp
ut
.to_str() == Some("-") 
 213         && opt
.hash_file
.as_ref().and_then(|h
| h
.to_str()) == Some("-") 
 215         return Err("Error: Cannot use use stdin for both hash file and input data".to_owned());