This is a simple module that helps you implement the Secure Remote Password (SRP) protocol in Elixir applications. For more information about SRP, see the design documents and RFC5054.
This package can be installed by adding strap
to your list of dependencies in
mix.exs
:
def deps do
[
{:strap, "~> 0.1.1"}
]
end
If you're running inside of a Phoenix application, you may need to ensure the Erlang crypto application is loaded.
def application do
[
extra_applications: [
:logger,
:crypto
]
]
end
The HexDocs.pm documentation is available here.
A typical SRP request/response flow looks like this:
-
Client gets username/password from user
-
Client -> Server: Client sends the username to the server.
-
Server looks up the user's information, from a database for example. This information would be the prime, generator, salt, and so-called verifier value for this user. Optionally, if the server doesn't know this user, it may return/calculate fake values to obscure the user's lack-of-presence.
-
Server generates a public value, based on the prime, generator, verifier, and an ephemeral randomly-generated private value only it knows.
-
Server -> Client: Server sends the prime, generator, salt, and public value back to the client.
-
Client generates a public value, based on the prime, generator, and an ephemeral randomly-generated private value only it knows.
-
Client -> Server: Client sends its public value to the server.
-
Server generates a pre-shared master key based upon the information it has.
-
Client generates a pre-shared master key based upon the information it has.
The server and client should, at this point, verify that their pre-shared master
keys match. For example, the client could send a HMAC(key, server-public-key)
to the server, and the server could send HMAC(key, client-public-key)
back
to the client.
Altenatively, if the preshared key will be utilized for further encrypted communication, not just authentication, the server and the client can simply exchange encrypted messages using an agreed-upon cipher (e.g. AES-256). A failure to decrypt messages indicates a lack of knowledge of the preshared key.
This library helps with steps 4, 6, 8, and 9, above. An example flow might look like:
# Client
username = get_username()
private_client_password = get_password()
# Server
# Fetch verifier and salt from database
{salt, private_server_verifier} = get_salt_and_verifier(username)
# Use "known-good" prime/generator; could also be stored in database
{prime, generator} = Strap.prime_group(2048)
server =
Strap.protocol(:srp6a, prime, generator)
|> Strap.server(verifier)
server_public_value = Strap.public_value(server)
# Client
client =
Strap.protocol(:srp6a, prime, generator)
|> Strap.client(username, private_client_password, salt)
client_public_vlaue = Strap.public_value(client)
# Server
{:ok, private_server_session_key} =
Strap.session_key(server, client_public_value)
# Client
{:ok, private_client_session_key} =
Strap.session_key(client, server_public_value)
# At this point, the following should be true:
^private_server_session_key = private_client_session_key