Imagine one was required to create a web-based password management system (over SSL! :) with the following requirements:
- Individual users sign in to the system using their own unique pass phrase.
- This pass phrase should be enough to allow the user to use the system effectively (e.g. from a smartphone, etc.)--the point being that they should not have to keep a key file with them.
- Users can store arbitrary-length bits of data in the system ("entries").
- Entries are encrypted in the database in such a way that there is not enough information in the database or application alone to read the encrypted entries.
- Users should be able to "share" entries with other users of the system so that the other user(s) can read the contents of the entry.
I'm no expert in cryptography. After thinking about it for a while, I came up with the following. My question is: is this implementation secure? Am I missing something? If so, is the above spec even implementable? Or is this overkill?
The database is set up as such:
+------------------------------------------------------------------------------+
| users |
+---------+--------------+--------------+---------------+----------------------+
| salt | pub_key | enc_priv_key | priv_key_hmac | |
+---------+--------------+--------------+---------------+----------------------+
| entries |
+---------+--------------+--------------+---------------+----------+-----------+
| user_id | parent_entry | enc_sym_key | sym_key_sig | enc_data | data_hmac |
+---------+--------------+--------------+---------------+----------+-----------+
Let's imagine two users of the system, Alice and Bob.
Bob signs up for the site:
- Bob enters a password. This password is sent to the server (but not stored).
- The server generates a random salt and stores it in the
salt
field. - The server generates the SHA-256 hash of Bob's password and salt.
- The server generates an RSA key pair. The public key is stored as plain
text in the
pub_key
field. The private key is encrypted via AES-256 using the hash generated from Bob's password and salt as the key and stored in theenc_priv_key
field. - The server generates a hash-based message authentication code for Bob's
private key using Bob's password and salt as the key and stores this in
the
priv_key_hmac
field.
Bob stores an entry in the system:
- Bob enters some data to be stored as an entry along with his password. This data is sent to the server.
- The server generates a key to be used as a key for AES-256 encryption.
- The server uses this key to encrypt the data and stores the result in
the
enc_data
field. - The server generates a hash-based message authentication code for the
data using the generated key and stores this in the
data_hmac
field. - The symmetric key used to encrypt the data is encrypted with Bob's public
key and stored in the
enc_sym_key
field. - The server uses Bob's private key to generate a signature for the symmetric key.
Bob retrieves his stored entry:
- Bob enters his password and the ID of the entry to retrieve.
- The server generates the SHA-256 hash of Bob's password and salt.
- Bob's encrypted private key is decrypted via AES-256 encryption using the hash.
- The server verifies that Bob's encrypted private key has not been
tampered with by checking the HMAC in
priv_key_hmac
. - The server decrypts the symmetric key stored in the
enc_sym_key
field using Bob's private key. - The server verifies that the encrypted symmetric key has not been tampered
with by verifying the signature in
sym_key_sign
using Bob's public key. - The server decrypts the data using the symmetric key.
- The server verifies that the encrypted data has not been tampered with
by verifying the HMAC stored in the
data_hmac
field. - The server returns the decrypted data to Bob.
Bob shares an entry with Alice:
- Bob wants Alice to have access to an entry he owns. He enters his password and the ID of the entry to share.
- The data for the entry is decrypted using the method in "Bob retrieves his stored entry."
- A new entry is created for Alice in the same fashion as in "Bob stores
an entry in the system," with the following exceptions:
- The entry's
parent_entry
is set to Bob's entry. - The signature for the symmetric key is calculated using Bob's private key (since Alice's private key is not available to Bob).
- When Alice accesses this new entry, the existence of a non-null
parent_entry
causes the system to use Bob's public key to verify the signature (since his private key was used to create it).
- The entry's
Bob changes the data in his shared entry:
- Bob decides to change the data in the entry he shared with Alice. Bob indicates the entry ID to modify and the new data it should contain.
- The system overwrites the data created in "Bob stores an entry in the system."
- The system finds every entry with a
parent_entry
equal to the entry that was just modified, and for each one overwrites the data created in "Bob shares an entry with Alice."
Advantages:
- It is impossible to decrypt any data from the database without the password of the user that owns the data, as the private key necessary to decrypt the data is encrypted with the user's password, and that password (and it's hash) is not stored in the database.
- If a user wants to change their password, only their encrypted private key needs to be regenerated (decrypt the private key with the old password/hash, then re-encrypt it with the new password/hash).
- Shared entries are stored as actual separate records in the database, so there is no need to share a key between multiple users/groups of users.
Disadvantages/Problems (that I can think of):
- If a shared entry is modified, the system must re-encrypt every child entry; with a large number of users sharing data, this could potentially be computationally expensive.
- Shared entries depend on the parent user's public key for signature verification. If the user is deleted, or their key changes, the signatures are invalid.
Repeated from the introduction: my question is: is this implementation secure? Am I missing something? If so, is the above spec even implementable? Or is this overkill?
Thanks!