• caglararli@hotmail.com
  • 05386281520

Deriving multiple hashes from a single password for different use cases

Çağlar Arlı      -    2 Views

Deriving multiple hashes from a single password for different use cases

I'm designing a service to store secrets without relying on traditional mail-password system.

I will describe this service to give a bit more context for my questions, at the end.

  • secret The payload we want to store (encrypted) for the user (size limited)
  • identifier A random value stored by the user (e.g., in a local file), required to retrieve the secret.
  • password A user-chosen password (may be weak).
  • pass_hash1 A deterministic hash derived from password used server-side to compute storage key.
  • pass_hash2 A deterministic hash derived from password used client-side to encrypt/decrypt the secret.
  • secret_id = hash(identifier + pass_hash1) Unique record key in the server’s database.
  • secret_encrypted = encryption(private_key: pass_hash2, payload: secret) The ciphertext of the secret using pass_hash2.

Store

  1. On the client side, we generate a random secure identifier, probably store in a file, and, the user define a password that may be weak.

  2. From the password we generate two hashes:

  • pass_hash1 for authentication
  • pass_hash2 to encrypt the secret
  1. The client encrypts his secret using pass_hash2 and make a store secret request to the server containing:
  • identifier
  • pass_hash1
  • secret_encrypted
  1. The server receive store secret request and generate a secret_id = hash(identifier + pass_hash1). Then, the server create a new database entry:
  • id: secret_id
  • created_at: DateTime.now()
  • value: secret_encrypted

Fetch

  1. The client, own a file that contains his identifier and know his password

  2. From the password we generate two hashes:

  • pass_hash1 for authentication
  • pass_hash2 to encrypt the secret
  1. The client make a fetch secret request to the server containing:
  • identifier
  • pass_hash1
  1. The server receive the fetch secret request an perform:
  • Checks an in-memory Map<identifier, DateTime?> to check if the secret for this identifier has already been requested recently. If not enough time elapsed the identifier remains rate-limited against targeted brute-force.
  • Compute secret_id = hash(identifier + pass_hash1) and lookup in the database for an entry, if something is found it returns the secret_encrypted else it add the identifier and Datetime.now() to the map to limit further attempts.
  1. The user can recover his secret_encrypted by deciphering it using pass_hash2 as encryption key

Privacy and security goals

A user can store multiple secrets and we shouldn't be able to link any secret to a specific user. Each secret has his own identifier.

This microservice shouldn't be able to answer the request give me the entry associated to this identifier. The identifier does not persist, it is temporary available in-memory map Map<identifier, DateTime?> in case of failed attempts on fetch secret. The map is cleared each time the server reboots. The identifier alone without pass_hash1 is not enough to compute the secret_id.

This microservice shouldn't be able to tell if many entries belongs to one or multiple users, we do not persist pass_hash1 it's processed at REST of the store secret request, then forgotten.

In case of database leak, attackers shouldn't be able to read the stored secrets. The secrets are encrypted using pass_hash2, the resistance to local brute-force depends on the quality of the user password.

The microservice shouldn't be able to read the secrets as well because they are encrypted.

Questions

  • Is there any existing standards close to what I'm trying to achieve (ProtonMail Key management server?) ?
  • Is it safe to derivate two hashes from the same password to be used in two different ways: authentication and encryption ?
  • I wanted to use deterministic hashing function (SHA-256, BLAKE2 …) over password hashing (bcrypt …) to avoid to deal with multiple output especially when I need to compute secret_id. Since hashes are never exposed, is it ok ?
  • Any other advice ?