From 9aaee506ede11fa4fb5d05c42e20efb2a7c80948 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 21 Dec 2022 03:15:23 -0500 Subject: [PATCH] hash uploaded file --- Cargo.lock | 16 ++++---- crates/lib/files_common/Cargo.toml | 5 ++- crates/lib/files_common/src/hash.rs | 39 +++++++++++++++++++ crates/lib/files_common/src/lib.rs | 1 + .../endpoints/media_uploads/upload_audio.rs | 17 +++++++- 5 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 crates/lib/files_common/src/hash.rs diff --git a/Cargo.lock b/Cargo.lock index bf5b198dad..97e96dfa2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1216,7 +1216,7 @@ dependencies = [ "log", "once_cell", "rand 0.8.4", - "ring 0.17.0-alpha.10", + "ring 0.17.0-alpha.11", "serde", "serde_json", "tempfile", @@ -1426,9 +1426,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "database-backup" @@ -1749,7 +1749,9 @@ dependencies = [ name = "files_common" version = "0.0.1" dependencies = [ + "data-encoding", "infer 0.11.0", + "ring 0.17.0-alpha.11", ] [[package]] @@ -2425,7 +2427,7 @@ dependencies = [ "regex", "reqwest", "reusable_types", - "ring 0.17.0-alpha.10", + "ring 0.17.0-alpha.11", "rust-s3", "serde", "serde_derive", @@ -3869,9 +3871,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.0-alpha.10" +version = "0.17.0-alpha.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ae9a4d2975bdd8254d7bcdd2261be62b42b8920f903ec682b08cdc11b87af2" +checksum = "4575a179070909595bea5f999d67934737c2e0757a1eb9839af555917817b257" dependencies = [ "cc", "libc", @@ -4590,7 +4592,7 @@ dependencies = [ "regex", "reqwest", "reusable_types", - "ring 0.17.0-alpha.10", + "ring 0.17.0-alpha.11", "rust-s3", "serde", "serde_derive", diff --git a/crates/lib/files_common/Cargo.toml b/crates/lib/files_common/Cargo.toml index 07311ba06a..8f1d525e62 100644 --- a/crates/lib/files_common/Cargo.toml +++ b/crates/lib/files_common/Cargo.toml @@ -13,8 +13,9 @@ name = "files_common" path = "src/lib.rs" [dependencies] -# Mimetype detection -infer = "0.11" +data-encoding = "2.3.3" # base64, hex, etc. +infer = "0.11" # Mimetype detection +ring = "0.17.0-alpha.11" # Crypto [dev-dependencies] # None yet diff --git a/crates/lib/files_common/src/hash.rs b/crates/lib/files_common/src/hash.rs new file mode 100644 index 0000000000..450f8374dd --- /dev/null +++ b/crates/lib/files_common/src/hash.rs @@ -0,0 +1,39 @@ +use data_encoding::HEXLOWER_PERMISSIVE; +use ring::digest::{Context, Digest, SHA256}; +use std::io::{BufReader, Read}; + +pub fn hash_bytes_sha2(bytes: &[u8]) -> std::io::Result { + let reader = BufReader::new(bytes); + let digest = sha256_digest(reader)?; + let hash = HEXLOWER_PERMISSIVE.encode(digest.as_ref()); + Ok(hash) +} + +fn sha256_digest(mut reader: R) -> std::io::Result { + let mut context = Context::new(&SHA256); + let mut buffer = [0; 1024]; + + loop { + let count = reader.read(&mut buffer)?; + if count == 0 { + break; + } + context.update(&buffer[..count]); + } + + Ok(context.finish()) +} + +#[cfg(test)] +mod tests { + use crate::hash::hash_bytes_sha2; + + #[test] + fn test_hash_bytes_sha2() { + assert_eq!(hash_bytes_sha2(&[]).unwrap(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + assert_eq!(hash_bytes_sha2(&[0, 0, 0, 0]).unwrap(), "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + assert_eq!(hash_bytes_sha2(&[0, 255, 0, 255]).unwrap(), "7a7bf454c5f3cb1b9d9a20f81417f98d976fe3b3dd52c1b9968f02e89e7e8a2f"); + assert_eq!(hash_bytes_sha2("testing".as_bytes()).unwrap(), "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a90"); + assert_eq!(hash_bytes_sha2("hash this".as_bytes()).unwrap(), "19467788bc0cf11790a075ea718452cecf0e79db59d1964670475e5fe2e4a611"); + } +} diff --git a/crates/lib/files_common/src/lib.rs b/crates/lib/files_common/src/lib.rs index a66b0885cb..fb5bf60d61 100644 --- a/crates/lib/files_common/src/lib.rs +++ b/crates/lib/files_common/src/lib.rs @@ -19,4 +19,5 @@ #![allow(dead_code)] #![allow(non_snake_case)] +pub mod hash; pub mod mimetype; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs index d2dbf95070..8b493586a4 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs @@ -26,6 +26,7 @@ use sqlx::error::Error::Database; use sqlx::mysql::MySqlDatabaseError; use std::fmt; use std::sync::Arc; +use files_common::hash::hash_bytes_sha2; use files_common::mimetype::{get_mimetype_for_bytes, get_mimetype_for_bytes_or_default}; use tokens::files::media_upload::MediaUploadToken; @@ -138,6 +139,20 @@ pub async fn upload_audio_handler( .map(|bytes| get_mimetype_for_bytes(bytes.as_ref())) .flatten(); + let maybe_hash = upload_media_request.file_bytes + .as_ref() + .map(|bytes| hash_bytes_sha2(bytes.as_ref())) + .transpose() + .map_err(|io_error| { + warn!("Problem hashing bytes: {:?}", io_error); + return UploadAudioError::ServerError; + })?; + + let hash = match maybe_hash { + None => return Err(UploadAudioError::BadInput("invalid file".to_string())), + Some(hash) => hash, + }; + let record_id = insert_media_upload(Args { token: &token, uuid_idempotency_token: &uuid_idempotency_token, @@ -150,7 +165,7 @@ pub async fn upload_audio_handler( maybe_original_video_encoding: None, maybe_original_frame_width: None, maybe_original_frame_height: None, - checksum_sha2: "", + checksum_sha2: &hash, public_bucket_directory_full_path: "", extra_file_modification_info: MediaUploadDetails {}, // TODO maybe_creator_user_token: maybe_user_token.as_ref(),