0

Edit: removed a part of the original question; I'll break it down in a different post.

I am following @Polynomial's answer to store information in a database. Here are my requirements:

  • Only the user can see their information. The server only sees scrambled data.
  • Allows for easy password changes
  • OK with "lost your password, lost your data"
  • If possible, doesn't store a hash of the user password
  • Use only one server and its own storage space

Here's what I have so far:

DB Table USER_DATA contains ([XORed key] [random IV] [encrypted secret data] [userID])

Data Entry

1. User enters (secret data) into a form.
2. User enters (user password)
3. Generate a strong (random key).
4. Encrypt (secret data) using (random key) and (random IV), as (encrypted secret data).
5. XOR hash of (random key) with hash of (user password) as (XORed key).
6. In the database, store in a new row: (XORed key),(random IV),(encrypted secret data), (userID)

Retrieve Data

1. Ask user for (user password).
2. Query database for (XORed key),(random IV),(encrypted secret data).
3. XOR hash (user password) with (XORed key) to retrieve the initial (random key)
4. Use (random key), (random IV) to decrypt (encrypted secret data).
5. Display decrypted data on screen.

I'd like to request feedback about the system above, regarding its (1) security and (2) efficiency. I would also like to get your thoughts about the following system:

Authentication

The simple way would be to store a hash of the password, authenticate against it, and create a session to allow browsing the records. Each record is decrypted and then displayed to the user. However, since the password is used to decrypt all user information, I'm trying to avoid storing its hash: I'm curious if it's even possible with only one server involved. Here is one system I thought of, but breaking it would take exactly the same effort of brute-forcing a hash:

Initial User Login

1. Ask user for (user password)
1. Create a strong (random key)
2. XOR (user password) with (random key) into (XORed key).
   Store this value in the database.
3. Create a (random hash).  Store in database.
4. Encrypt (random hash) with (random key) and (random IV)
   as (Encrypted random hash).  Store in database.

So now we have in the database: Table USER_AUTH ([XORed key] [random hash] [Encrypted random hash] [random IV] )

In subsequent logins, the password the user supplies is XORed with (XORed key) to retrieve (random key). (random key) is then used to decrypt (Encrypted random hash). If the decrypted value is the same as (random hash), then the password is correct.

  • Why try to avoid storing the password hash? Also, XOR a random key with the password is not very strong, as the password is likely shorter than the key. Is it necessary that users are able to recover data after a password reset, or are you okay with a "lose your password, lose your data" model? – David May 08 '14 at 05:55
  • @David: edited my question to reflect: I'm OK with a "lose your password, lose your data" model. Should also mention that I'm XORing a "strong" hash of the random key with a hash of the password, so they are the same length. – David_Springfield May 08 '14 at 14:10

2 Answers2

1

What you probably want to do is use a Key Wrap algorithm, but it's not absolutely necessary. It seems reasonable to me that you could do the following for encryption:

  1. Compute a user key from the user's password, using something strong like PBKDF2.
  2. Generate a random data key.
  3. Encrypt the data key with the user key, this will be stored as the wrapped key.
  4. Generate an HMAC across the wrapped key keyed by the user key. This will be stored for authentication. (You still need to select the row by another mechanism.)
  5. Encrypt the data using the data key.
  6. Store the PBKDF2 salt, wrapped key, hmac, IV, and encrypted data.

Decryption:

  1. Select the relevant row from the DB.
  2. Using the salt, recalculate the user key from the password.
  3. Verify the HMAC on the wrapped key using the user key. This is the authentication step.
  4. Use the user key to decrypt the wrapped key, revealing the data key.
  5. Decrypt the data using the IV and the data key.
David
  • 15,814
  • 3
  • 48
  • 73
0

I see two flaws with this system:

1) You don't mention any way to associate rows in the database with a given user (ie. step 2 in the "retrieve data" process).

2) Every random key is XORd with the same value (the user's password). Since the random keys can be assumed to be uniformly distributed, an attacker who can determine that multiple database rows belong to the same user can retrieve the user's password through trivial statistical analysis. The more data the user stores, the easier and more accurate this analysis is.

Mark
  • 34,390
  • 9
  • 85
  • 134
  • @Mark- edited the question to mention the User ID column in the database. Also- in (2) you're making a great point, but isn't randomly creating the IV and re-computing a random key for each row defeats that statistical analysis? My impression was that, because the keys are randomly generated every time, the distribution should not be very uniform. – David_Springfield May 08 '14 at 14:16
  • @David_Springfield Unless you're doing something to ensure the password hash is different for each row, the "XORed key" column is simply a series of [XOR ciphered messages](https://en.wikipedia.org/wiki/XOR_cipher) where the key is constant. "Uniform" refers to the [statistical distribution](https://en.wikipedia.org/wiki/Uniform_distribution_(discrete)), and if your keys aren't uniformly distributed, you've introduced a weakness that can be used to attack your system. – Mark May 08 '14 at 19:35
  • Mark- I've realized that I requested answers to multiple questions in my post, so I broke it down to several different questions that I'll post separately. thanks. – David_Springfield May 09 '14 at 14:51