blob: b8ac0f7c0d67a5c335aad6b77bfe7b884167508d [file] [log] [blame]
// Copyright 2015 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern crate ring;
use ring::*;
use std::error::Error;
use std::io::{Read, Write};
fn print_usage(program_name: &str) {
let program_file_name = std::path::Path::new(program_name)
.file_name().unwrap().to_str().unwrap();
println!(
"Usage: {} sha256|sha384|sha512 <digest value in hex> <filename>\n\
\n\
On success nothing is output, and 0 is returned.\n\
On failure, an error message is printed, and a non-zero value is returned\n\
\n\
Example:\n\
{} sha256 \
def7352915ac84bea5e2ed16f6fff712d35de519799777bf927e2a567ab53b7e \
LICENSE",
program_file_name, program_file_name);
}
fn run(digest_name: &str, expected_digest_hex: &str,
file_path: &std::path::Path) -> Result<(), &'static str> {
let digest_alg = match digest_name {
"sha256" => &digest::SHA256,
"sha384" => &digest::SHA384,
"sha512" => &digest::SHA512,
_ => { return Err("unsupported digest algorithm"); }
};
let mut ctx = digest::Context::new(digest_alg);
{
let mut file = match std::fs::File::open(file_path) {
Ok(file) => file,
// TODO: don't use panic here.
Err(why) => panic!("couldn't open {}: {}", file_path.display(),
why.description())
};
let mut chunk = vec![0u8; 128 * 1024];
loop {
match file.read(&mut chunk[..]) {
Ok(0) => break,
Ok(bytes_read) => ctx.update(&chunk[0..bytes_read]),
// TODO: don't use panic here
Err(why) => panic!("couldn't open {}: {}", file_path.display(),
why.description())
}
}
}
let actual_digest = ctx.finish();
let matched = match from_hex(&expected_digest_hex) {
Ok(expected) => actual_digest.as_ref() == &expected[..],
Err(msg) => panic!("syntactically invalid digest: {} in {}", msg,
&expected_digest_hex),
};
match matched {
true => Ok(()),
false => Err("digest mismatch") // TODO: calculated digest.
}
}
pub fn from_hex(hex_str: &str) -> Result<Vec<u8>, String> {
if hex_str.len() % 2 != 0 {
return Err(
String::from("Hex string does not have an even number of digits"));
}
fn from_hex_digit(d: u8) -> Result<u8, String> {
if d >= b'0' && d <= b'9' {
Ok(d - b'0')
} else if d >= b'a' && d <= b'f' {
Ok(d - b'a' + 10u8)
} else if d >= b'A' && d <= b'F' {
Ok(d - b'A' + 10u8)
} else {
Err(format!("Invalid hex digit '{}'", d as char))
}
}
let mut result = Vec::with_capacity(hex_str.len() / 2);
for digits in hex_str.as_bytes().chunks(2) {
let hi = from_hex_digit(digits[0])?;
let lo = from_hex_digit(digits[1])?;
result.push((hi * 0x10) | lo);
}
Ok(result)
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.iter().any(|arg| arg == "-h") {
print_usage(&args[0]);
return
} else if args.len() < 4 {
print_usage(&args[0]);
std::process::exit(1);
}
match run(&args[1], &args[2], std::path::Path::new(&args[3])) {
Ok(x) => x,
Err(s) => {
let _ = writeln!(&mut std::io::stderr(), "{}", s);
std::process::exit(1)
}
}
}