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
126
127
128
129
pub use self::hmac_ring::Hmac;

mod hmac_ring {
    use key::Store;
    use primitives::Primitive;

    use data_encoding::base64;
    use ring::{digest, hkdf, hmac, rand};
    use serde_mcf::Hashes;

    use std::fmt;

    /// Password storage strengthening using HMAC.
    ///
    /// This struct holds the parameters used.
    /// Represents the `ring` implementation.
    pub struct Hmac {
        key: hmac::SigningKey,
        key_id: [u8; 32],
    }

    impl Hmac {
        /// Construct a new `Hmac` instance with a specified key
        pub fn with_key(h: &'static digest::Algorithm, key: &[u8]) -> Primitive {
            let mut key_id = [0_u8; 32];
            key_id.copy_from_slice(digest::digest(&digest::SHA512_256, key).as_ref());
            Self {
                    key: hmac::SigningKey::new(h, key),
                    key_id: key_id,
                }
                .into()
        }

        /// Gets a default HMAC instance, generating a fresh new key.
        pub fn default() -> Primitive {
            Self::new().into()
        }

        fn new() -> Self {
            let rng = rand::SystemRandom::new();
            let mut key_id = [0_u8; 32];
            let key = hmac::SigningKey::generate_serializable(&digest::SHA256, &rng, &mut key_id)
                .expect("could not generate random bytes for key");
            let digest = digest::digest(&digest::SHA512_256, &key_id);
            ::key::KEY_STORE.insert(base64::encode_nopad(digest.as_ref()), &key_id[..]);
            key_id.copy_from_slice(digest.as_ref());
            Self {
                key: key,
                key_id: key_id,
            }
        }

        fn key_id(&self) -> String {
            base64::encode_nopad(&self.key_id)
        }
    }

    impl ::primitives::PrimitiveImpl for Hmac {
        /// Compute the scrypt hash
        fn compute(&self, password: &[u8], _salt: &[u8]) -> Vec<u8> {
            let mut hash = vec![0_u8; 32];
            hkdf::extract_and_expand(&self.key, password, b"libpasta password hashing", &mut hash);
            hash
        }

        /// Convert parameters into a vector of (key, value) tuples
        /// for serializing.
        fn params_as_vec(&self) -> Vec<(&'static str, String)> {
            vec![("key_id", self.key_id()),
                 ("h", super::super::hash_to_id(self.key.digest_algorithm()))]
        }

        fn hash_id(&self) -> Hashes {
            Hashes::Hmac
        }
    }

    impl fmt::Debug for Hmac {
        fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            write!(f,
                   "Hmac, KeyID: {}, Hash: {}",
                   self.key_id(),
                   super::super::hash_to_id(self.key.digest_algorithm()))
        }
    }
}


#[cfg(test)]
mod test {
    use ::hashing::*;
    use serde_mcf;

    #[test]
    fn sanity_check() {
        let password = "hunter2";
        let hmac_params = super::Hmac::default();
        println!("{:?}", hmac_params);
        let inner_params = ::primitives::scrypt::Scrypt::default();
        let salt = ::get_salt();
        let hash = hmac_params.compute(&inner_params.compute(password.as_bytes(), &salt), &salt);
        let hash2 = hmac_params.compute(&inner_params.compute(password.as_bytes(), &salt), &salt);
        let params = Algorithm::Nested {
            outer: hmac_params.into(),
            inner: Box::new(Algorithm::Single(inner_params.into())),
        };
        assert_eq!(hash, hash2);
        let out = Output {
            alg: params,
            salt: salt,
            hash: hash,
        };
        println!("{:?}", serde_mcf::to_string(&out).unwrap());
    }

    #[test]
    fn hash_verify_works() {
        let password = "hunter2";
        let algorithm = Algorithm::Nested {
            outer: super::Hmac::default().into(),
            inner: Box::new(Algorithm::Single(::primitives::Scrypt::default())),
        };
        let hash = algorithm.hash(password.to_string().into());
        assert!(hash.verify(password.to_string().into()));
    }

}

benches!(Hmac);