kitab

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | LICENSE

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 }