Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some suggestions about the library API #3

Open
Fi5t opened this issue Jul 16, 2020 · 3 comments
Open

Some suggestions about the library API #3

Fi5t opened this issue Jul 16, 2020 · 3 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@Fi5t
Copy link
Contributor

Fi5t commented Jul 16, 2020

Hello! I'm really glad to see an Android version of the Argon2 library. I wanted to do it myself, but I haven't found enough time for it. Thanks for having done it for me 馃槈

However, when I started using this library I found out, that some API weren't very convenient to use. For instance:

  1. Passing a mode parameter to the verify() method looks redundant because it can be elicited from a hash.
val hashResult : Argon2KtResult = argon2Kt.hash(
  mode = Argon2Mode.ARGON2_I,
 ....
)

val verificationResult : Boolean = argon2Kt.verify(
  mode = Argon2Mode.ARGON2_I,
  ...
)
  1. The verify() method accepts only the string representation of the hash. It would be better to add an additional version of this method that would accept the byte array representation of the hash. It's very convenient to use it when reading hash from a file.
val hashResult : Argon2KtResult = argon2Kt.hash(
  mode = Argon2Mode.ARGON2_I,
 ....
)

storage.openFileOutput().use {
  it.write(hash.encodedOutputAsByteArray())
}

...

val storedHash = file.openFileInput().use { it.readBytes() }

val verificationResult : Boolean = argon2Kt.verify(
  mode = Argon2Mode.ARGON2_I,
  encoded = storedHash,
  password = passwordByteArray,
)
  1. It would be nice to have an ability to reconstruct Argon2KtResult from the String/ByteArray representation. I have no idea how to do it now without using reflection.

Maybe I'll face some other things later, but these things are the most important ones for me. I understand that you may not have time to make these API changes, but I can send you PR with my own implementation. What do you think?

@lambdapioneer
Copy link
Owner

Hi @Fi5t , I'm very glad that my library is helpful for you. Thanks so much for trying it out and the suggestions :)

Absolutely happy to review pull-requests for all of these (ideally a separate one for each point). The suggested changes look very sensible!

1. Sounds great. And it seems that this improvement is even applicable to the underlying argon2 library. See encoding.c:

 *  $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>
[...]
int decode_string(argon2_context *ctx, const char *str, argon2_type type) {

The proper fix would be to update it there and adding the type to the argon2_context. But I am also happy to diverge with Argon2Kt here (either in Kotlin or the C++ code) to save time. Might be worth creating an issue in the main project afterwards though.

2. Yes, more convenience is always good. I'd suggest that we go all the way and have each choice (String, ByteArray, ByteBuffer) for both the encoded and password parameter.

3. I am not sure what you mean with reflection. I'd suggest to add a Java_com_lambdapioneer_argon2kt_Argon2Jni_nativeArgon2decodeString in the Argon2Jni.cpp that calls the decode_string string method of the C library to extract the raw hash. Happy to look into it the next week if it's important for you.

@Fi5t
Copy link
Contributor Author

Fi5t commented Jul 17, 2020

I think that the core agron2 library is very "low-level" and it can make sense for such types of libraries API. But libraries intended to the client code need to have a "high-level" API. So, my suggestion about the simplification of the verify() method might make sense only for your library. I've already implemented this feature in my library, which uses your library.

Here is my approach:

private val hashModeRegex = Regex("""argon2(i?d?)""")

....

return argon2instance.verify(
    parseHashMode(hashAsString),
    hashAsString,
    passphraseOrPin
)

....

private fun parseHashMode(encodedHash: String): Argon2Mode {
    val res = hashModeRegex.find(encodedHash)

    return when (res?.value) {
        "argon2i" -> Argon2Mode.ARGON2_I
        "argon2d" -> Argon2Mode.ARGON2_D
        "argon2id" -> Argon2Mode.ARGON2_ID
        else -> throw BadHashException()
    }
}

Decoding a hash to Argon2KtResult is not a mission-critical feature for me. It would be nice to have, no more.

@lambdapioneer
Copy link
Owner

lambdapioneer commented Jul 17, 2020

Sounds good. Just one quick suggestions, by using Regex("""^\${'$'}argon2(i?d?)""") we ensure that we only match it when it's at the beginning of the line -- see the extra ^ for begin of line and then the ugly magic for escaping the $ sign.

I'm also happy with just three String.startsWith("$argo ... calls as the when conditions. I feel simplicity wins here and compared to the main algorithm our computations are basically non-existent.

@lambdapioneer lambdapioneer added enhancement New feature or request good first issue Good for newcomers labels Mar 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants