diff --git a/src/api/server/invite.rs b/src/api/server/invite.rs index edf80cd6..0ceb914f 100644 --- a/src/api/server/invite.rs +++ b/src/api/server/invite.rs @@ -1,7 +1,7 @@ use axum::extract::State; use axum_client_ip::InsecureClientIp; use base64::{engine::general_purpose, Engine as _}; -use conduit::{err, utils, warn, Err, Error, PduEvent, Result}; +use conduit::{err, utils, utils::hash::sha256, warn, Err, Error, PduEvent, Result}; use ruma::{ api::{client::error::ErrorKind, federation::membership::create_invite}, events::room::member::{MembershipState, RoomMemberEventContent}, @@ -160,7 +160,7 @@ pub(crate) async fn create_invite_route( ruma::api::appservice::event::push_events::v1::Request { events: vec![pdu.to_room_event()], txn_id: general_purpose::URL_SAFE_NO_PAD - .encode(utils::calculate_hash(&[pdu.event_id.as_bytes()])) + .encode(sha256::hash(pdu.event_id.as_bytes())) .into(), ephemeral: Vec::new(), to_device: Vec::new(), diff --git a/src/core/utils/hash.rs b/src/core/utils/hash.rs index 5a3664cb..c12d4f66 100644 --- a/src/core/utils/hash.rs +++ b/src/core/utils/hash.rs @@ -1,13 +1,10 @@ mod argon; -mod sha256; +pub mod sha256; use crate::Result; -pub fn password(password: &str) -> Result { argon::password(password) } - -pub fn verify_password(password: &str, password_hash: &str) -> Result<()> { +pub fn verify_password(password: &str, password_hash: &str) -> Result { argon::verify_password(password, password_hash) } -#[must_use] -pub fn calculate_hash(keys: &[&[u8]]) -> Vec { sha256::hash(keys) } +pub fn password(password: &str) -> Result { argon::password(password) } diff --git a/src/core/utils/hash/sha256.rs b/src/core/utils/hash/sha256.rs index b2e5a94c..06e210a7 100644 --- a/src/core/utils/hash/sha256.rs +++ b/src/core/utils/hash/sha256.rs @@ -1,9 +1,62 @@ -use ring::{digest, digest::SHA256}; +use ring::{ + digest, + digest::{Context, SHA256, SHA256_OUTPUT_LEN}, +}; -#[tracing::instrument(skip_all, level = "debug")] -pub(super) fn hash(keys: &[&[u8]]) -> Vec { - // We only hash the pdu's event ids, not the whole pdu - let bytes = keys.join(&0xFF); - let hash = digest::digest(&SHA256, &bytes); - hash.as_ref().to_owned() +pub type Digest = [u8; SHA256_OUTPUT_LEN]; + +/// Sha256 hash (input gather joined by 0xFF bytes) +#[must_use] +#[tracing::instrument(skip(inputs), level = "trace")] +pub fn delimited<'a, T, I>(mut inputs: I) -> Digest +where + I: Iterator + 'a, + T: AsRef<[u8]> + 'a, +{ + let mut ctx = Context::new(&SHA256); + if let Some(input) = inputs.next() { + ctx.update(input.as_ref()); + for input in inputs { + ctx.update(b"\xFF"); + ctx.update(input.as_ref()); + } + } + + ctx.finish() + .as_ref() + .try_into() + .expect("failed to return Digest buffer") +} + +/// Sha256 hash (input gather) +#[must_use] +#[tracing::instrument(skip(inputs), level = "trace")] +pub fn concat<'a, T, I>(inputs: I) -> Digest +where + I: Iterator + 'a, + T: AsRef<[u8]> + 'a, +{ + inputs + .fold(Context::new(&SHA256), |mut ctx, input| { + ctx.update(input.as_ref()); + ctx + }) + .finish() + .as_ref() + .try_into() + .expect("failed to return Digest buffer") +} + +/// Sha256 hash +#[inline] +#[must_use] +#[tracing::instrument(skip(input), level = "trace")] +pub fn hash(input: T) -> Digest +where + T: AsRef<[u8]>, +{ + digest::digest(&SHA256, input.as_ref()) + .as_ref() + .try_into() + .expect("failed to return Digest buffer") } diff --git a/src/core/utils/mod.rs b/src/core/utils/mod.rs index b8640f3a..18c2dd6f 100644 --- a/src/core/utils/mod.rs +++ b/src/core/utils/mod.rs @@ -28,7 +28,7 @@ pub use self::{ bytes::{increment, u64_from_bytes, u64_from_u8, u64_from_u8x8}, debug::slice_truncated as debug_slice_truncated, future::TryExtExt as TryFutureExtExt, - hash::calculate_hash, + hash::sha256::delimited as calculate_hash, html::Escape as HtmlEscape, json::{deserialize_from_str, to_canonical_object}, math::clamp, diff --git a/src/service/rooms/state/mod.rs b/src/service/rooms/state/mod.rs index 7d8200f0..29ffedfc 100644 --- a/src/service/rooms/state/mod.rs +++ b/src/service/rooms/state/mod.rs @@ -157,12 +157,7 @@ impl Service { let previous_shortstatehash = self.get_room_shortstatehash(room_id).await; - let state_hash = calculate_hash( - &state_ids_compressed - .iter() - .map(|s| &s[..]) - .collect::>(), - ); + let state_hash = calculate_hash(state_ids_compressed.iter().map(|s| &s[..])); let (shortstatehash, already_existed) = self .services diff --git a/src/service/rooms/state_compressor/mod.rs b/src/service/rooms/state_compressor/mod.rs index f0c851de..0466fb12 100644 --- a/src/service/rooms/state_compressor/mod.rs +++ b/src/service/rooms/state_compressor/mod.rs @@ -352,12 +352,7 @@ impl Service { .await .ok(); - let state_hash = utils::calculate_hash( - &new_state_ids_compressed - .iter() - .map(|bytes| &bytes[..]) - .collect::>(), - ); + let state_hash = utils::calculate_hash(new_state_ids_compressed.iter().map(|bytes| &bytes[..])); let (new_shortstatehash, already_existed) = self .services diff --git a/src/service/sending/sender.rs b/src/service/sending/sender.rs index f5d87504..ee818289 100644 --- a/src/service/sending/sender.rs +++ b/src/service/sending/sender.rs @@ -539,16 +539,13 @@ impl Service { } } - let txn_id = &*general_purpose::URL_SAFE_NO_PAD.encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEvent::Edu(b) => &**b, - SendingEvent::Pdu(b) => b.as_ref(), - SendingEvent::Flush => &[], - }) - .collect::>(), - )); + let txn_hash = calculate_hash(events.iter().filter_map(|e| match e { + SendingEvent::Edu(b) => Some(&**b), + SendingEvent::Pdu(b) => Some(b.as_ref()), + SendingEvent::Flush => None, + })); + + let txn_id = &*general_purpose::URL_SAFE_NO_PAD.encode(txn_hash); //debug_assert!(pdu_jsons.len() + edu_jsons.len() > 0, "sending empty // transaction"); @@ -664,23 +661,21 @@ impl Service { //debug_assert!(pdu_jsons.len() + edu_jsons.len() > 0, "sending empty // transaction"); - let transaction_id = &*general_purpose::URL_SAFE_NO_PAD.encode(calculate_hash( - &events - .iter() - .map(|e| match e { - SendingEvent::Edu(b) => &**b, - SendingEvent::Pdu(b) => b.as_ref(), - SendingEvent::Flush => &[], - }) - .collect::>(), - )); + + let txn_hash = calculate_hash(events.iter().filter_map(|e| match e { + SendingEvent::Edu(b) => Some(&**b), + SendingEvent::Pdu(b) => Some(b.as_ref()), + SendingEvent::Flush => None, + })); + + let txn_id = &*general_purpose::URL_SAFE_NO_PAD.encode(txn_hash); let request = send_transaction_message::v1::Request { origin: self.server.config.server_name.clone(), pdus: pdu_jsons, edus: edu_jsons, origin_server_ts: MilliSecondsSinceUnixEpoch::now(), - transaction_id: transaction_id.into(), + transaction_id: txn_id.into(), }; let client = &self.services.client.sender; @@ -692,7 +687,7 @@ impl Service { .iter() .filter(|(_, res)| res.is_err()) .for_each( - |(pdu_id, res)| warn!(%transaction_id, %server, "error sending PDU {pdu_id} to remote server: {res:?}"), + |(pdu_id, res)| warn!(%txn_id, %server, "error sending PDU {pdu_id} to remote server: {res:?}"), ); }) .map(|_| dest.clone())