digest.rs (7511B)
1 use std::marker::Copy; 2 use std::io::Read; 3 use std::fmt; 4 use std::str::FromStr; 5 6 use crate::error::ParseError; 7 8 use sha2::{ 9 Sha512, 10 Sha256, 11 Digest, 12 }; 13 14 use log::error; 15 16 #[derive(Copy, Clone)] 17 pub enum DigestType { 18 Sha512, 19 Sha256, 20 #[cfg(feature="digest_md5")] 21 MD5, 22 } 23 24 impl FromStr for DigestType { 25 type Err = ParseError; 26 fn from_str(s: &str) -> Result<DigestType, Self::Err> { 27 match s { 28 "md5" => { 29 return Ok(DigestType::MD5); 30 }, 31 "sha512" => { 32 return Ok(DigestType::Sha512); 33 }, 34 "sha256" => { 35 return Ok(DigestType::Sha256); 36 }, 37 _ => { 38 return Err(ParseError::new("unknown digest string")); 39 }, 40 }; 41 } 42 } 43 44 impl DigestType { 45 pub fn digest_for(&self, f: impl Read) -> RecordDigest { 46 RecordDigest::Empty 47 } 48 } 49 50 /// Encapsulations of supported digests for digest data. 51 pub enum RecordDigest { 52 Sha512(Vec<u8>), 53 Sha256(Vec<u8>), 54 MD5(Vec<u8>), 55 SwarmHash(Vec<u8>), 56 EmptyWithType(DigestType), 57 Empty, 58 } 59 60 impl Clone for RecordDigest { 61 fn clone(&self) -> RecordDigest { 62 match self { 63 RecordDigest::Sha512(v) => { 64 RecordDigest::Sha512(v.to_vec()) 65 }, 66 RecordDigest::Sha256(v) => { 67 RecordDigest::Sha256(v.to_vec()) 68 }, 69 RecordDigest::MD5(v) => { 70 RecordDigest::MD5(v.to_vec()) 71 }, 72 RecordDigest::SwarmHash(v) => { 73 RecordDigest::SwarmHash(v.to_vec()) 74 }, 75 _ => { 76 RecordDigest::Empty 77 }, 78 } 79 } 80 } 81 82 impl RecordDigest { 83 pub fn fingerprint(&self) -> Vec<u8> { 84 match self { 85 RecordDigest::Sha512(v) => { 86 return v.to_vec(); 87 }, 88 RecordDigest::Sha256(v) => { 89 return v.to_vec(); 90 }, 91 RecordDigest::MD5(v) => { 92 return v.to_vec(); 93 }, 94 RecordDigest::SwarmHash(v) => { 95 return v.to_vec(); 96 }, 97 _ => { 98 return vec!() 99 }, 100 } 101 } 102 103 /// Returns the digest value of the media as a hex-encoded string. 104 /// 105 /// TODO: implememt in fmt for digest instead 106 pub fn urn(&self) -> String { 107 match self { 108 RecordDigest::Sha512(v) => { 109 return String::from("sha512:") + hex::encode(&v).as_str(); 110 }, 111 RecordDigest::Sha256(v) => { 112 return String::from("sha256:") + hex::encode(&v).as_str(); 113 }, 114 RecordDigest::MD5(v) => { 115 return String::from("md5:") + hex::encode(&v).as_str(); 116 }, 117 RecordDigest::SwarmHash(v) => { 118 return hex::encode(&v); 119 }, 120 _ => { 121 return String::new(); 122 }, 123 } 124 } 125 } 126 127 impl fmt::Debug for RecordDigest { 128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 129 write!(f, "{}", self.urn()) 130 } 131 } 132 133 /// Create a [RecordDigest::Sha512](RecordDigest::Sha512) instance from the raw digest data. 134 /// 135 /// Will fail if digest has incorrect length. 136 pub fn from_vec(v: Vec<u8>) -> Result<RecordDigest, ParseError> { 137 let sz = Sha512::output_size(); 138 if v.len() != sz { 139 return Err(ParseError::new("invalid digest size")); 140 } 141 Ok(RecordDigest::Sha512(v)) 142 } 143 144 /// Create a [RecordDigest](RecordDigest) instance corresponding to the URN digest scheme. 145 /// 146 /// Valid URN schemes and their corresponding enumerated values are: 147 /// 148 /// * `sha512` -> [RecordDigest::Sha512](RecordDigest::Sha512]) 149 /// * `sha256` -> [RecordDigest::Sha256](RecordDigest::Sha256]) 150 /// * `bzz` -> [RecordDigest::SwarmHash](RecordDigest::SwarmHash]) 151 pub fn from_urn(urn: &str) -> Result<RecordDigest, ParseError> { 152 let mut v = urn.split(":"); 153 let r = match v.next() { 154 Some("sha512") => { 155 let digest_hex = match v.next() { 156 Some(r) => { 157 r 158 }, 159 None => { 160 return Err(ParseError::new("not a valid digest urn")); 161 }, 162 }; 163 let digest = hex::decode(digest_hex).unwrap(); 164 match from_vec(digest) { 165 Ok(vv) => { 166 vv 167 }, 168 Err(e) => { 169 return Err(ParseError::new("invalid sha512 digest")); 170 }, 171 } 172 }, 173 Some("sha256") => { 174 let digest_hex = v.next().unwrap(); 175 let digest = hex::decode(digest_hex).unwrap(); 176 177 let sz = Sha256::output_size(); 178 if digest.len() != sz { 179 return Err(ParseError::new("invalid sha256 digest")); 180 } 181 182 RecordDigest::Sha256(digest) 183 }, 184 Some("md5") => { 185 let digest_hex = match v.next() { 186 Some(r) => { 187 r 188 }, 189 None => { 190 return Err(ParseError::new("not a valid digest urn")); 191 }, 192 }; 193 let digest = hex::decode(digest_hex).unwrap(); 194 195 if digest.len() != 16 { 196 return Err(ParseError::new("invalid md5 digest")); 197 } 198 199 RecordDigest::MD5(digest) 200 }, 201 Some("bzz") => { 202 let digest_hex = v.next().unwrap(); 203 let digest = hex::decode(digest_hex).unwrap(); 204 205 if digest.len() != 32 { 206 return Err(ParseError::new("invalid bzz digest")); 207 } 208 209 RecordDigest::SwarmHash(digest) 210 }, 211 Some("") => { 212 RecordDigest::Empty 213 }, 214 Some(_) => { 215 return Err(ParseError::new("unknown digest type")); 216 }, 217 None => { 218 RecordDigest::Empty 219 }, 220 }; 221 Ok(r) 222 } 223 224 #[cfg(test)] 225 mod tests { 226 use super::from_urn; 227 use super::ParseError; 228 229 #[test] 230 fn test_digest_urn_parse() { 231 match from_urn("sha512:deadbeef") { 232 Ok(v) => { 233 panic!("expected fail"); 234 }, 235 _ => {}, 236 }; 237 match from_urn("sha512:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") { 238 Ok(v) => {}, 239 _ => { 240 panic!("expected pass"); 241 }, 242 }; 243 match from_urn("sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") { 244 Ok(v) => {}, 245 _ => { 246 panic!("expected pass"); 247 }, 248 }; 249 match from_urn("bzz:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") { 250 Ok(v) => {}, 251 _ => { 252 panic!("expected pass"); 253 }, 254 }; 255 match from_urn("foo:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") { 256 Ok(v) => { 257 panic!("expected fail"); 258 }, 259 _ => {}, 260 }; 261 match from_urn("foo:deadbeef") { 262 Ok(v) => { 263 panic!("expected fail"); 264 }, 265 _ => {}, 266 }; 267 match from_urn("") { 268 Ok(v) => {}, 269 _ => { 270 panic!("expected pass"); 271 }, 272 }; 273 } 274 }