Build Log 6: Password Auth with scrypt

A project log for Network Detached Storage Device

Ever wanted a self-hosting NAS robust enough you can throw it in your rucksack and forget about it?

sdfgeoffsdfgeoff 09/13/2022 at 09:490 Comments

After some fiddling we have the ability to create a user and to see a fail/success page depending on if you enter the password correctly!

There is no session management, so you don't "stay logged in" at all, but I'm pleased to get here.

Cryptography is weird. Why? Well, my password DB looks like:

1	oweritu     {"version": 1, "salt": "USj+tEYk7h8LthQi85qLXhD45uc4zh89EagdzirL9Vw=", "secret": "MgKvFO3L8AqVff4pfQ2iUoDNL7/B1O6XaPJ1kOSBUG/3dlanGMF6dQF4rDh+P2eqoiSFvBkvV9Nlf6g/CTTueg=="}
2	testUser    {"version": 1, "salt": "/wyJHxr2tXThl5gKUUj7zk9/+quqh2EhCDnN/fA2CDM=", "secret": "vuWRHq1Zcxw6S74t+bITH9wpZLMYIV4B8UPUz50tlwwRevXJAQ7lv9inlPzkgt/MGTcQxZb0C0bEIYDps9xCCg=="}
3	newUser     {"version": 1, "salt": "eEmsv1mi6QEhpSRNQ3W7+nqYr3o6yf97H2HZW+S655M=", "secret": "ZxQHddv9XMiZS0QecUW56mkEiE6GySp60KTRZzZGrz9SJ54Y7AeGg9Dh98w5s9PCTAtlApHNkBpfMqaSThfHqQ=="}

(pulled straight from sqlite). 

And I'm using the functions:

def encode_password_v1(password: bytes) -> bytes:
    salt = os.urandom(32)

    raw_secret = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=4, dklen=64)

    bundle = {
        "version": 1,
        "salt": base64.b64encode(salt).decode("utf-8"),
        "secret": base64.b64encode(raw_secret).decode("utf-8"),
    return json.dumps(bundle).encode("utf-8")

def validate_password_v1(password: bytes, secret_bundle: bytes) -> bool:
    bundle = json.loads(secret_bundle.decode("utf-8"))
    assert bundle["version"] == 1

    salt = base64.b64decode(bundle["salt"])
    target_secret = base64.b64decode(bundle["secret"])

    this_secret = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=4, dklen=64)
    return this_secret == target_secret

And even with all this information ... you still shouldn't be able to retrieve the username/password for those users. You have all the same data needed to prove that a user is who they say they are, but not enough to figure out what their magic password is that they're using to do so.

At least, that's the theory. Do I trust my auth code? Well, enough for a non-internet-facing application. If this project was intended to be wider-web facing I'd probably want someone to give the code a review. So how about this: I legitimately have no idea what password I used for the user "oweritu". If you can provide me a password that works for that user and the above password DB, I'll paypal you $5. If you continue and help me make those functions more secure, then I'll paypal you an additional $15 (aka $20 total).

Disclaimer: I am a complete security noob! Easy money for someone I'm sure (and hopefully a learning experience for me)

Oh yeah, and the code for all this is up on the project's github


What's next? Sessions! Cookies!