Skip to content

Commit

Permalink
[fix] #3370: Serialize hash as hex string (#3421)
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
  • Loading branch information
mversic authored Apr 25, 2023
1 parent d5fbb2a commit b185545
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 41 deletions.
2 changes: 1 addition & 1 deletion configs/peer/genesis.json

Large diffs are not rendered by default.

66 changes: 34 additions & 32 deletions crypto/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
#[cfg(not(feature = "std"))]
use alloc::{borrow::ToOwned as _, format, string::String, vec, vec::Vec};
use core::{hash, marker::PhantomData, num::NonZeroU8};
use alloc::{
borrow::ToOwned as _,
format,
string::{String, ToString as _},
vec,
vec::Vec,
};
use core::{hash, marker::PhantomData, num::NonZeroU8, str::FromStr};

use derive_more::{DebugCustom, Deref, DerefMut, Display};
use iroha_ffi::FfiType;
use iroha_schema::{IntoSchema, TypeId};
use parity_scale_codec::{Decode, Encode};
use serde::{Deserialize, Serialize};
use serde_with::DeserializeFromStr;
#[cfg(feature = "std")]
use ursa::blake2::{
digest::{Update, VariableOutput},
VarBlake2b,
};

use crate::ffi;
use crate::{ffi, hex_decode, Error};

ffi::ffi_item! {
/// Hash of Iroha entities. Currently supports only blake2b-32.
/// The least significant bit of hash is set to 1.
#[derive(
DebugCustom,
Display,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
TypeId
)]
#[display(fmt = "{}", "hex::encode(self.as_ref())")]
#[debug(fmt = "{}", "hex::encode(self.as_ref())")]
#[allow(clippy::unsafe_derive_deserialize)] // NOTE: Unsafe invariants are maintained in `FromStr`
#[derive(DebugCustom, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, DeserializeFromStr, TypeId)]
#[display(fmt = "{}", "hex::encode_upper(self.as_ref())")]
#[debug(fmt = "{}", "hex::encode_upper(self.as_ref())")]
#[repr(C)]
pub struct Hash {
more_significant_bits: [u8; Self::LENGTH - 1],
Expand All @@ -56,10 +53,10 @@ impl Hash {
/// Wrap the given bytes; they must be prehashed with `VarBlake2b`
pub fn prehashed(mut hash: [u8; Self::LENGTH]) -> Self {
hash[Self::LENGTH - 1] |= 1;
#[allow(unsafe_code)]
// SAFETY:
// - any `u8` value after bitwise or with 1 will be at least 1
// - `Hash` and `[u8; Hash::LENGTH]` have the same memory layout
#[allow(unsafe_code)]
unsafe {
core::mem::transmute(hash)
}
Expand Down Expand Up @@ -121,20 +118,7 @@ impl Serialize for Hash {
S: serde::Serializer,
{
let hash: &[u8; Self::LENGTH] = self.as_ref();
hash.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for Hash {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error as _;
<[u8; Self::LENGTH]>::deserialize(deserializer)
.and_then(|hash| {
Hash::is_lsb_1(&hash)
.then_some(hash)
.ok_or_else(|| D::Error::custom("expect least significant bit of hash to be 1"))
})
.map(Self::prehashed)
hex::encode_upper(hash).serialize(serializer)
}
}

Expand Down Expand Up @@ -165,6 +149,24 @@ impl Encode for Hash {
}
}

impl FromStr for Hash {
type Err = Error;

fn from_str(key: &str) -> Result<Self, Self::Err> {
let hash: [u8; Self::LENGTH] = hex_decode(key)?.try_into().map_err(|hash_vec| {
Error::Parse(format!(
"Unable to parse {hash_vec:?} as [u8; {}]",
Self::LENGTH
))
})?;

Hash::is_lsb_1(&hash)
.then_some(hash)
.ok_or_else(|| Error::Parse("expect least significant bit of hash to be 1".to_owned()))
.map(Self::prehashed)
}
}

impl Decode for Hash {
fn decode<I: parity_scale_codec::Input>(
input: &mut I,
Expand Down
9 changes: 1 addition & 8 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,15 +516,8 @@ impl<'de> Deserialize<'de> for PrivateKey {
}
}

/// Shim for decoding hexadecimal strings that can have spaces
/// Shim for decoding hexadecimal strings
pub(crate) fn hex_decode<T: AsRef<[u8]> + ?Sized>(payload: &T) -> Result<Vec<u8>, Error> {
let payload: Vec<u8> = payload
.as_ref()
.iter()
.filter(|&e| *e as char != ' ')
.copied()
.collect();

hex::decode(payload).map_err(|err| Error::Parse(err.to_string()))
}

Expand Down

0 comments on commit b185545

Please sign in to comment.