Closed jimmycuadra closed 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.
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));
HEAD as of 486738ab addresses this. Feel free to provide further feedback.
Lovely! Thanks so much!
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.
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.
@bryant Any thoughts on my last comment? Is there a better way to do it that I misunderstand?
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.
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.
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?
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.
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?
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.
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.
That's great—thanks bryant!
you have renamed Verifier
to Encoded
, but kept the namespace verifier
. I think it should be renamed too, what do you think?
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?