From b9bb01a284ec25d9bc51fb881a924b4781e83867 Mon Sep 17 00:00:00 2001 From: Zac Mrowicki Date: Thu, 7 May 2020 20:25:16 +0000 Subject: [PATCH] Add `from_path()` to Target This function allows one to get a Target object from a given Path, provided it is a file. --- tough/src/schema/error.rs | 21 +++++++++++++++++++ tough/src/schema/mod.rs | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/tough/src/schema/error.rs b/tough/src/schema/error.rs index 577e9ea1..a89eb85e 100644 --- a/tough/src/schema/error.rs +++ b/tough/src/schema/error.rs @@ -5,6 +5,7 @@ use crate::schema::RoleType; use snafu::{Backtrace, Snafu}; use std::fmt::{self, Debug, Display}; +use std::path::PathBuf; /// Alias for `Result`. pub type Result = std::result::Result; @@ -17,6 +18,22 @@ pub enum Error { #[snafu(display("Duplicate key ID: {}", keyid))] DuplicateKeyId { keyid: String }, + /// Unable to open a file + #[snafu(display("Failed to open '{}': {}", path.display(), source))] + FileOpen { + path: PathBuf, + source: std::io::Error, + backtrace: Backtrace, + }, + + /// Unable to read the file + #[snafu(display("Failed to read '{}': {}", path.display(), source))] + FileRead { + path: PathBuf, + source: std::io::Error, + backtrace: Backtrace, + }, + /// A downloaded target's checksum does not match the checksum listed in the repository /// metadata. #[snafu(display("Invalid key ID {}: calculated {}", keyid, calculated))] @@ -72,6 +89,10 @@ pub enum Error { /// Failed to extract a bit string from a `SubjectPublicKeyInfo` document. #[snafu(display("Invalid SubjectPublicKeyInfo document"))] SpkiDecode { backtrace: Backtrace }, + + /// Unable to create a TUF target from anything but a file + #[snafu(display("TUF targets must be files, given: '{}'", path.display()))] + TargetNotAFile { path: PathBuf, backtrace: Backtrace }, } /// Wrapper for error types that don't impl [`std::error::Error`]. diff --git a/tough/src/schema/mod.rs b/tough/src/schema/mod.rs index 29d2ac69..6ee6bfb8 100644 --- a/tough/src/schema/mod.rs +++ b/tough/src/schema/mod.rs @@ -15,12 +15,16 @@ use crate::schema::iter::KeysIter; use crate::schema::key::Key; use chrono::{DateTime, Utc}; use olpc_cjson::CanonicalFormatter; +use ring::digest::{Context, SHA256}; use serde::{Deserialize, Serialize}; use serde_json::Value; use serde_plain::{forward_display_to_serde, forward_from_str_to_serde}; use snafu::ResultExt; use std::collections::HashMap; +use std::fs::File; +use std::io::Read; use std::num::NonZeroU64; +use std::path::Path; /// A role type. #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)] @@ -224,6 +228,45 @@ pub struct Target { pub _extra: HashMap, } +impl Target { + /// Given a path, returns a Target struct + pub fn from_path

(path: P) -> Result + where + P: AsRef, + { + // Ensure the given path is a file + let path = path.as_ref(); + if !path.is_file() { + return error::TargetNotAFile { path }.fail(); + } + + // Get the sha256 and length of the target + let mut file = File::open(path).context(error::FileOpen { path })?; + let mut digest = Context::new(&SHA256); + let mut buf = [0; 8 * 1024]; + let mut length = 0; + loop { + match file.read(&mut buf).context(error::FileRead { path })? { + 0 => break, + n => { + digest.update(&buf[..n]); + length += n as u64; + } + } + } + + Ok(Target { + length, + hashes: Hashes { + sha256: Decoded::from(digest.finish().as_ref().to_vec()), + _extra: HashMap::new(), + }, + custom: HashMap::new(), + _extra: HashMap::new(), + }) + } +} + impl Role for Targets { const TYPE: RoleType = RoleType::Targets;