Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Can someone help me understand why the author of the python library chose to raise an exception on verification failures as opposed to returning false?


Two reasons:

1. mainly: the C library has no concept of “wrong password”; only “verification failed with an error”. If you want to know why it failed, you need the error. As you can see in the example, a wrong password is "Decoding failed” which can also be your fault. It seems like they want to interpret their own failures as little possible. Therefore raising an exception with the error seemed the best way forward. 2. secondarily: in security context, I tend to prefer loud failures for dangerous problems so they don’t pass unnoticed by accident. ymmv


On that topic, if it's raising an exception in the middle of testing the hash, could this permit a timing attack?


It isn’t raised in the middle of testing the hash. The testing is completely done in the Argon2 C library and the bindings raise an error if it returns an error. The Python library doesn’t do anything smart at all except calling C functions on strings.


Comparisons after hashing are naturally resistant to timing attacks, because you are not in direct control of the bytes being compared.

Just ask Bitcoin miners how hard it is to pick an input which results in a hash with a desired n-bit prefix.

But as a belt-and-suspenders you often see an attempt at fixed time comparisons of digests in any case.

Coincidentally, hashing before comparing can be used in scripting languages where the compare function will often be optimized out from under you, making constant time compare difficult or impossible to actually guarantee.


A core tenet of Python is that "it's easier to ask for forgiveness than permission":

https://docs.python.org/2/glossary.html#term-eafp


This principle doesn't necessarily mean functions should never return booleans, though.

Booleans are used in a variety of (popular) Python libraries when checking whether a password is correct (e.g. Django's `check_password` returns False if the password is wrong).


This is most likely the reason. It allows your code to follow the happy path for logging in, and treat verification as the exceptional case. Definitely a design choice by the author who self-identifies as a Pythonista.

  def handle_login():
    try:
      ph.verify(hash, "s3kr3tp4ssw0rd")
      log_login()
      redirect_to_page()
    except VerificationError:
      log_bad_login()
      redirect_to_login()


While we're talking Python best practices, I highly recommend using an `else` clause to keep the code being 'excepted' to one line:

  def handle_login():
    try:
      ph.verify(hash, "s3kr3tp4ssw0rd")
    except VerificationError:
      log_bad_login()
      redirect_to_login()
    else:
      log_login()
      redirect_to_page()
It's not a big deal for this code, but in general this is good practice because

1. It makes it very obvious to the next developer which line is the one that is expected to raise that exception

2. One of the other lines could unintentionally raise that exception and mistakenly trigger the except clause. (This is more of an issue with Python's built in exceptions than with something very specific like this `VerificationError` example.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: