1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::assert_length;
use crate::error::HoloHashError;
use crate::HashType;
use crate::HoloHash;
use crate::PrimitiveHashType;
use crate::HOLO_HASH_CORE_LEN;
use crate::HOLO_HASH_FULL_LEN;
use crate::HOLO_HASH_PREFIX_LEN;
use std::convert::TryFrom;
use std::convert::TryInto;
impl<P: PrimitiveHashType> TryFrom<&str> for HoloHash<P> {
type Error = HoloHashError;
fn try_from(s: &str) -> Result<Self, HoloHashError> {
let hash_type = P::new();
HoloHash::from_raw_39(holo_hash_decode(hash_type.get_prefix(), s)?)
}
}
impl<P: PrimitiveHashType> TryFrom<&String> for HoloHash<P> {
type Error = HoloHashError;
fn try_from(s: &String) -> Result<Self, HoloHashError> {
Self::try_from(s as &str)
}
}
impl<P: PrimitiveHashType> TryFrom<String> for HoloHash<P> {
type Error = HoloHashError;
fn try_from(s: String) -> Result<Self, HoloHashError> {
Self::try_from(&s)
}
}
impl<T: HashType> std::fmt::Display for HoloHash<T> {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{}", holo_hash_encode(self.get_raw_39()))
}
}
pub fn holo_hash_encode(data: &[u8]) -> String {
format!("u{}", base64::encode_config(data, base64::URL_SAFE_NO_PAD),)
}
pub fn holo_hash_decode_unchecked(s: &str) -> Result<Vec<u8>, HoloHashError> {
if &s[..1] != "u" {
return Err(HoloHashError::NoU);
}
let s = match base64::decode_config(&s[1..], base64::URL_SAFE_NO_PAD) {
Err(_) => return Err(HoloHashError::BadBase64),
Ok(s) => s,
};
if s.len() != HOLO_HASH_FULL_LEN {
return Err(HoloHashError::BadSize);
}
let loc_bytes = holo_dht_location_bytes(
&s[HOLO_HASH_PREFIX_LEN..HOLO_HASH_PREFIX_LEN + HOLO_HASH_CORE_LEN],
);
let loc_bytes: &[u8] = &loc_bytes;
if loc_bytes != &s[HOLO_HASH_PREFIX_LEN + HOLO_HASH_CORE_LEN..] {
return Err(HoloHashError::BadChecksum);
}
assert_length!(HOLO_HASH_FULL_LEN, &s);
Ok(s.to_vec())
}
pub fn holo_hash_decode(prefix: &[u8], s: &str) -> Result<Vec<u8>, HoloHashError> {
if &s[..1] != "u" {
return Err(HoloHashError::NoU);
}
let s = match base64::decode_config(&s[1..], base64::URL_SAFE_NO_PAD) {
Err(_) => return Err(HoloHashError::BadBase64),
Ok(s) => s,
};
if s.len() != HOLO_HASH_FULL_LEN {
return Err(HoloHashError::BadSize);
}
let actual_prefix: [u8; HOLO_HASH_PREFIX_LEN] = s[..HOLO_HASH_PREFIX_LEN].try_into().unwrap();
if actual_prefix != prefix {
return Err(HoloHashError::BadPrefix(
format!("{:?}", prefix),
actual_prefix,
));
}
let loc_bytes = holo_dht_location_bytes(
&s[HOLO_HASH_PREFIX_LEN..HOLO_HASH_PREFIX_LEN + HOLO_HASH_CORE_LEN],
);
let loc_bytes: &[u8] = &loc_bytes;
if loc_bytes != &s[HOLO_HASH_PREFIX_LEN + HOLO_HASH_CORE_LEN..] {
return Err(HoloHashError::BadChecksum);
}
assert_length!(HOLO_HASH_FULL_LEN, &s);
Ok(s.to_vec())
}
pub fn holo_dht_location_bytes(data: &[u8]) -> Vec<u8> {
assert_eq!(32, data.len(), "only 32 byte hashes supported");
let hash = blake2b_128(data);
let mut out = vec![hash[0], hash[1], hash[2], hash[3]];
for i in (4..16).step_by(4) {
out[0] ^= hash[i];
out[1] ^= hash[i + 1];
out[2] ^= hash[i + 2];
out[3] ^= hash[i + 3];
}
out
}
pub fn blake2b_256(data: &[u8]) -> Vec<u8> {
let hash = blake2b_simd::Params::new().hash_length(32).hash(data);
hash.as_bytes().to_vec()
}
pub fn blake2b_128(data: &[u8]) -> Vec<u8> {
let hash = blake2b_simd::Params::new().hash_length(16).hash(data);
hash.as_bytes().to_vec()
}