0

I'm making a little media server, just for fun, but I want to know if what I'm thinking makes sense.

The idea is that the files will be stored on the server, encrypted. The file encryption key (henceforth FEK) will also be stored on the server, but it too will be encrypted using a 2nd key, derived from the user's password (henceforth PDK).

It will work like this:

  1. There will be a 2-stage login. First, the user enters their username.
  2. If the user exists, the server will send back two fixed, per-user salts and a # of iterations.
  3. The client will use the first salt to hash their password with 1 iteration of sha256. They will send this to the server.
  4. The server takes this hashed password and rehashes with a 3rd salt and many iterations (PBKDF2) and uses this to authenticate the user.
  5. Meanwhile, the client uses the 2nd salt and # of iterations that the server previously sent, also with PBKDF2, to create the PDK.
  6. If the user is successfully authenticated, the server sends back the encrypted key (FEK).
  7. The client uses the PDK to decrypt the FEK.
  8. The decrypted FEK is used to encrypt and decrypt all files client-side.

Rationale: The password will be sent over HTTPS, but supposing my server is completely compromised (attacker has SSH access), then they could just wait for someone to login and get the plain-text password. One iteration of sha256 doesn't provide a lot of protection, but this is a sort of worst-case scenario, and I don't want to double the amount of time we're waiting for PBKDF2 (once on client, again on server).

It still has to be rehashed on the server in case the attacker manages to download the database, but maybe doesn't have full/persistent access. This would prevent them from cracking passwords too quickly.

I sent the 2nd salt and # of iterations to the client before they authenticate so that we can do (4) and (5) in parallel. Salt and # iterations don't really need to be secret, do they?

Since the server never sees the plain-text password, it can never decrypt the FEK, which is the most important.

We use different salts to basically get 2 different keys out of 1 password. One is the hashed password for authentication, and the other is the PDK.

I also want to do file-deduplication using hashes. I realize this also exposes some information, so I plan on using the FEK for the file hashes too. Then I can only do per-user file deduplication, but that's good enough.

If the user changes their password, I simply re-encrypt the FEK and send it back to the server for safe-keeping. No need to re-encrypt the files.

Does this all make sense? Any problems in my plan?

Oh, also I'm thinking aes-256-cbc with per-file IVs for the encryption, HMAC-SHA256 for the file hashes, PBKDF2 w/ sha256 and half a second worth of iterations for both the password hash and the PDK.

1 Answer 1

2

Since you consider this more of a "code review", or a "security review", than something actually practical, here are my thoughts on the system:

Information Disclosure Vulnerability

Step 2 is an information disclosure vulnerability. An attacker can easily discern which usernames are legit, and which ones are not.

It's better to always do all the steps for authentication, even if the user does not exist. You can see this, for instance, when you try to log into your Google account. Even if the e-Mail address that you specified does not exist, they still ask for your password.

Overly complicated authentication scheme

Your threat model behind the authentication scheme is that you assume that an attacker may eventually gain root access on your system. In this case, yes, the attacker will not gain access to the plaintext password, but instead access to an unsalted SHA-256 hash.

Why? Because the attacker can modify the server to send an empty string as the first salt, causing the client to calculate a hash with an empty salt. It is marginally better than plaintext, but not by a lot.

If this marginal benefit in a very fringe scenario is worth an overly complicated authentication scheme is for you to decide. I recommend against it and to stick with a simpler, more traditional authentication scheme. There is a reason it's considered best practice.

Overly complicated FEK/PDK scheme

Your entire FEK/PDK scheme seems to be much too complicated. It also could be solved much simpler with asymmetric cryptography.

Encrypt the file with a unique key (DEK, Data Encryption Key) and encrypt that key with the public key of the user. The user can then decrypt the DEK with their private key and decrypt the data.

Not a Zero-Knowledge System

Since the server is presumably the one who generates the FEK, the server could theoretically store an unencrypted copy of it to access the data. The goal of a Zero-Knowledge system is to show users that the server has no knowledge of something. Your system requires trust that the server actually does what it claims to do, which is not the same.

Don't use Cipher Block Chaining Mode

AES-CBC has quite a lot of problems, and there is really no reason to use it. AES-GCM is actually faster than AES-CBC + HMAC-SHA1.

AES Benchmark

Source: Kazoo.ga


All in all, your scheme isn't bad, but it certainly seems a bit over-engineered.

4
  • I don't see any reason the client can't generate the FEK. The reason I keep it on the server is so that the user doesn't have to hold on to it. I want it so that the only thing they need is their username+password. If they can remember those 2 things, they're good. With your scheme, where is the private key stored?
    – mpen
    Commented Sep 13, 2019 at 8:08
  • The private key always remains with the user. It never leaves their machine. This is the essence of public-key cryptography. And if the client is supposed to encrypt the file with the FEK, then the client needs all the logic for this as well.
    – user163495
    Commented Sep 13, 2019 at 8:10
  • Hmm, I don't think that meets my requirements for not making the user keep a key. What do you mean by "traditional authentication scheme"? Just rely on HTTPS to keep the password safe?
    – mpen
    Commented Sep 13, 2019 at 8:29
  • You can let the user generate a public+private key pair and store the private key as a file on your client. Depending on the OS, there may even be a built-in way to do this. As for authentication, yes, use PBKDF2 or any other KDF (Argon2 is a good recommendation) and store that hash.
    – user163495
    Commented Sep 13, 2019 at 8:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .