bryant / argon2rs

The pure-Rust password hashing library running on Argon2.
MIT License
174 stars 19 forks source link

Password verification #2

Closed jimmycuadra closed 8 years ago

jimmycuadra commented 8 years ago

I might be missing something obvious, but does this library support password verification (i.e. argon2i_verify)? If not, are there plans to add it?

bryant commented 8 years ago

Am I correct in assuming that "verification" entails hashing a password and subsequently performing a constant-time compare against another (known) hash value? If so, an extra API entry point could be added for this, for sure.

jimmycuadra commented 8 years ago

Yes, that's right. The C library (and some of the other language bindings to it) provide a function where you can compare a plaintext value to a previously created hash to determine if they represent the same plaintext. You're able to do this without providing the salt that was used for the original hash. In pseudocode:

hash = argon2i("password", random_salt());
assert!(argon2i_verify("password", hash));
assert!(!argon2i_verify("wrong", hash));
bryant commented 8 years ago

HEAD as of 486738ab addresses this. Feel free to provide further feedback.

jimmycuadra commented 8 years ago

Lovely! Thanks so much!

jimmycuadra commented 8 years ago

Any chance of a new release to crates.io? I'd like to use the verifier without using a Git dependency if possible. Thanks for your work on this.

jimmycuadra commented 8 years ago

I'm also having some trouble figuring out the verification API. Would it be possible to add some documentation and examples to it?

The hash produced by Argon2::hash does not seem to be suitable as input to Verifier::from_u8 (the former seems like random bytes (it fails to be processed by std::str::from_utf8) and the latter seems to expect a byte string in the "$argon2..." format). How do I construct a verifier from a previously-created hash?

Edit: Looking at https://github.com/P-H-C/phc-winner-argon2, it looks like these two forms are what they call "raw hash" and "crypt-like encoding." Since argon2rs's verifier seems to only have a constructor that takes the encoded form, I guess what is not clear is how to generate the encoded form in the first place, since Argon2::hash seems to output the "raw hash."

Edit 2: Ah ha! I was able to verify a password with this code:

extern crate argon2rs;
extern crate rand;

use argon2rs::{Argon2, Variant};
use argon2rs::verifier::Verifier;
use rand::{OsRng, Rng};

pub fn genpass(password: &str) -> Vec<u8> {
    let argon2 = Argon2::default(Variant::Argon2i);
    let verifier = Verifier::new(argon2, password.as_bytes(), &gensalt(), &[], &[]);

    verifier.to_u8()
}

pub fn gensalt() -> [u8; 16] {
    let mut rng = OsRng::new().unwrap();
    let mut salt = [0u8; 16];

    rng.fill_bytes(&mut salt);

    salt
}

pub fn verifypass(encoded: &[u8], password: &str) -> bool {
    let verifier = Verifier::from_u8(encoded).unwrap();
    verifier.verify(password.as_bytes())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let encoded = genpass("secret");
        assert!(verifypass(&encoded, "secret"));
        assert!(!verifypass(&encoded, "wrong"));
    }
}

However, I still don't see a way of getting the encoded value using just the Argon2 struct by itself. I have to hash the password in the first place by hashing it implicitly by creating a verifier and then calling to_u8 on it. Very unintuitive—can this be improved? Maybe there should be an Argon2::encoded_hash method.

jimmycuadra commented 7 years ago

@bryant Any thoughts on my last comment? Is there a better way to do it that I misunderstand?

bryant commented 7 years ago

However, I still don't see a way of getting the encoded value using just the Argon2 struct by itself.

Why do you expect this to be the case? Verification is entirely orthogonal to the implementation details of the hash algorithm. The design of library reflects this: Verification is solely dealt with in argon2rs::verifier::*.

I'll have some documentation and examples soon, before the next crates.io release.

jimmycuadra commented 7 years ago

My expectation would be that if I create a hash with Argon2, I can later verify it with Verifier. But Verifier takes as input values that seem to be only constructed by Verifier in the first place.

bryant commented 7 years ago

The hash output of Argon2 is literally just that -- a sequence of raw hash bytes that has no concept of the original hash parameters. How can you expect to verify without knowing those parameters?

jimmycuadra commented 7 years ago

I understand what input is needed to verify. All I was trying to say is that I thought it would be a more intuitive API if there was a way for Argon2 to produce a value which could later be verified by Verifier. I would expect a type called Verifier to verify things, but I would not expect that it was necessary to also use it to create the values it would verify. I suggested the possibility of a method like Argon2::encoded_hash to produce a verifiable value from Argon2. In any case, it sounds like you're not convinced that is better, which is fine. Having documentation explain that Verifier is needed to create verifiable values should be sufficient.

bryant commented 7 years ago

Now that I think about it, you're right that the name "Verifier" is a bit of a misnomer. At the time of writing, my understanding had grouped both the generation of encoded forms and verification of existing ones under the term, "verification."

Would Encoded be a better name for this, perhaps?

jimmycuadra commented 7 years ago

That'd probably be fine. Once there are docs that explain that you need the encoded form in order to verify, it'll be much better. It was harder for me since I was figuring this out from reading the source code. I'd suggest that a note be added to the doc comment for Argon2::hash that you should use Verifier or whatever it gets renamed to instead if you need to verify the value later.

bryant commented 7 years ago

I've included a call in the Encoded API to encode with the default Argon2 params, so it should be more "intuitive": https://github.com/bryant/argon2rs/commit/e67d965d53dff78c587bcec5754ac837122fcbd0 . Confer examples/verifier.rs for details.

jimmycuadra commented 7 years ago

That's great—thanks bryant!

chpio commented 7 years ago

you have renamed Verifier to Encoded, but kept the namespace verifier. I think it should be renamed too, what do you think?