SRP v0.2.0 SRP View Source

SRP provides an implementation of the Secure Remote Password Protocol presented on The SRP Authentication and Key Exchange System, Using the Secure Remote Password (SRP) Protocol for TLS Authentication and The Secure Remote Password Protocol.

The protocol provides a way to do zero-knowledge authentication between client and servers. By using the SRP protocol you can:

  • authenticate without ever sending a password over the network.
  • authenticate without the risk of anyone learning any of your secrets – even if they intercept your communication.
  • authenticate both the identity of the client and the server to guarantee that a client isn’t communicating with an impostor server.

Signing up

After the user provides his username and password, the client must generate a password verifier. Then it must send to the server:

  • The username for future identification.
  • The password verifier that will be used in the future to verify the client credentials.
  • The salt used in the process.
username = "alice"
password = "password123"

identity = SRP.new_identity(username, password)
%SRP.IdentityVerifier{username: username, salt: salt, password_verifier: password_verifier} =
  SRP.generate_verifier(identity)

# Send to the server -> username + salt + password_verifier
# Server stores the information

Logging in

Authenticating a user on the server involves multiple steps.

  1. The client sends to the server the username.
  2. The server finds the password verifier and salt for that username. Then it generates a ephemeral key pair and sends back to the client the salt and the public key.
# Find the record for the given username
# Load from the database the password_verifier, and the salt
key_pair = SRP.server_key_pair(password_verifier)

# Send back to the client -> key_pair.public + salt

If the username does not exist the server can send a fake value. It is important to not reveal if an username is registered on the system or not. An attacker could use the login to find the registered usernames and try a dictionary attack specific for those users.

  1. The client generates a key pair and a client proof of identity. Then the client sends to the server the proof and the client’s public key.
# receives from the server the server_public_key and the salt.

identity = SRP.new_identity(username, password)
key_pair = SRP.client_key_pair()
proof = SRP.client_proof(identity, salt, key_pair, server_public_key)

# Send to the server -> proof + server_public_key
  1. Server verify client proof then build its own proof of identity. Then sends back the server’s proof.
valid? = SRP.valid_client_proof?(client_proof, password_verifier, server_key_pair, client_public_key)

if valid? do
  # Send back to client the server's proof -> server_proof
else
  # Send back unauthorized
end
  1. The client receives the server’s proof and validates it. This step can be skipped if you don’t feel the need to verify the server’s identity.
identity = SRP.new_identity(username, password)
valid? = SRP.valid_server_proof?(server_proof, identity, salt, client_key_pair, server_public_key)

From now on is to safe to create a new session between the client and server.

Prime Groups

The default prime size is 2048. Each prime group contains a large prime and a generator. These two values are used to derive several values on the calculations defined by the RFC.

The 1024-, 1536-, and 2048-bit groups are taken from software developed by Tom Wu and Eugene Jhong for the Stanford SRP distribution, and subsequently proven to be prime. The larger primes are taken from More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE), but generators have been calculated that are primitive roots of N, unlike the generators in More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE).

The following prime sizes are supported by SRP:

  • 1024
  • 1536
  • 2048
  • 3072
  • 4096
  • 6144
  • 8192

Hash Algorithm

By default the algorithm is SHA-1 because it is the algorithm used on the RFC. The SRP protocol uses a hash function to derive several values:

  • The hash of the public keys prevents an attacker who learns a user’s verifier from being able to authenticate as that user.
  • The hash of the prime group prevents an attacker who can select group parameters from being able to launch a 2-for-1 guessing attack.
  • Another hash contains the user’s password mixed with a salt.

Cryptanalytic attacks against SHA-1 that only affect its collision- resistance do not compromise these uses. If attacks against SHA-1 are discovered that do compromise these uses, new cipher suites should be specified to use a different hash algorithm.

The following hash algorithms are supported by SRP:

  • sha
  • sha224
  • sha256
  • sha384
  • sha512
  • md4
  • md5

Shared options

Almost all of the srp function below accept the following options:

  • :prime_size - The size of the prime to be used on the calculations (default: 2048);
  • :hash_algorithm - The hash algorithm used to derive several values (default: :sha);
  • :random_bytes - Quantity of random bytes used internally (default: 32)

Link to this section Summary

Functions

Generate a ephemeral key pair for the client. The private key is randomly generated and then the public key is derived from the private key

Generate a identity verifier that should be passed to the server during account creation

Generate a ephemeral key pair for the server. The private key is randomly generated, and the public key is derived from the private key and the password verifier

Link to this section Functions

Link to this function client_key_pair(options \\ []) View Source
client_key_pair(Keyword.t()) :: SRP.KeyPair.t()

Generate a ephemeral key pair for the client. The private key is randomly generated and then the public key is derived from the private key.

Examples

iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.client_key_pair()
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true

iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.client_key_pair(hash_algorithm: :sha512)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true

iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.client_key_pair(prime_size: 1024)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true

iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.client_key_pair(prime_size: 8192, hash_algorithm: :sha256)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true
Link to this function client_proof(identity, salt, client_key_pair, server_public_key, options \\ []) View Source
client_proof(binary(), binary(), SRP.KeyPair.t(), binary(), Keyword.t()) ::
  binary()

Generate a proof of identity for the client.

Examples

iex> identity = SRP.new_identity("bob", "password123")
iex> identity_verifier = SRP.generate_verifier(identity)
iex> client_key_pair = SRP.client_key_pair()
iex> server_key_pair = SRP.server_key_pair(identity_verifier.password_verifier)
iex> proof =
...>   SRP.client_proof(
...>     identity,
...>     identity_verifier.salt,
...>     client_key_pair,
...>     server_key_pair.public
...>   )
iex> is_binary(proof)
true
Link to this function generate_verifier(identity, options \\ []) View Source
generate_verifier(SRP.Identity.t(), Keyword.t()) :: SRP.IdentityVerifier.t()

Generate a identity verifier that should be passed to the server during account creation.

Examples

iex> alice_identity = SRP.new_identity("alice", "password123")
iex> %SRP.IdentityVerifier{username: "alice", salt: salt, password_verifier: password_verifier} =
...>   SRP.generate_verifier(alice_identity)
iex> is_binary(salt)
true
iex> is_binary(password_verifier)
true

iex> bob_identity = SRP.new_identity("bob", "password123")
iex> %SRP.IdentityVerifier{username: "bob", salt: salt, password_verifier: password_verifier} =
...>   SRP.generate_verifier(bob_identity, hash_algorithm: :sha512)
iex> is_binary(salt)
true
iex> is_binary(password_verifier)
true

iex> kirk_identity = SRP.new_identity("kirk", "password123")
iex> %SRP.IdentityVerifier{username: "kirk", salt: salt, password_verifier: password_verifier} =
...>   SRP.generate_verifier(kirk_identity, prime_size: 1024)
iex> is_binary(salt)
true
iex> is_binary(password_verifier)
true

iex> spock_identity = SRP.new_identity("spock", "password123")
iex> %SRP.IdentityVerifier{username: "spock", salt: salt, password_verifier: password_verifier} =
...>   SRP.generate_verifier(spock_identity, prime_size: 8192, hash_algorithm: :sha256)
iex> is_binary(salt)
true
iex> is_binary(password_verifier)
true
Link to this function new_identity(username, password) View Source

Create a new SRP.Identity struct.

Examples

iex> SRP.new_identity("alice", "password123")
%SRP.Identity{username: "alice", password: "password123"}
Link to this function server_key_pair(password_verifier, options \\ []) View Source
server_key_pair(binary(), Keyword.t()) :: SRP.KeyPair.t()

Generate a ephemeral key pair for the server. The private key is randomly generated, and the public key is derived from the private key and the password verifier.

Examples

iex> password_verifier = Base.decode16!("BEB25379D1A8581EB5A727673A2441EE")
iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.server_key_pair(password_verifier)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true

iex> password_verifier = Base.decode16!("BEB25379D1A8581EB5A727673A2441EE")
iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.server_key_pair(password_verifier, hash_algorithm: :sha512)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true

iex> password_verifier = Base.decode16!("BEB25379D1A8581EB5A727673A2441EE")
iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.server_key_pair(password_verifier, prime_size: 1024)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true

iex> password_verifier = Base.decode16!("BEB25379D1A8581EB5A727673A2441EE")
iex> %SRP.KeyPair{public: public_key, private: private_key} =
...>   SRP.server_key_pair(password_verifier, prime_size: 8192, hash_algorithm: :sha256)
iex> is_binary(public_key)
true
iex> is_binary(private_key)
true
Link to this function server_proof(client_proof, password_verifier, server_key_pair, client_public_key, options \\ []) View Source
server_proof(binary(), binary(), SRP.KeyPair.t(), binary(), Keyword.t()) ::
  binary()

Generate a proof of identity for the server.

Examples

iex> identity = SRP.new_identity("bob", "password123")
iex> identity_verifier = SRP.generate_verifier(identity)
iex> client_key_pair = SRP.client_key_pair()
iex> server_key_pair = SRP.server_key_pair(identity_verifier.password_verifier)
iex> client_proof =
...>   SRP.client_proof(
...>     identity,
...>     identity_verifier.salt,
...>     client_key_pair,
...>     server_key_pair.public
...>   )
iex> server_proof =
...>   SRP.server_proof(
...>     client_proof,
...>     identity_verifier.password_verifier,
...>     server_key_pair,
...>     client_key_pair.public
...>   )
iex> is_binary(server_proof)
true
Link to this function valid_client_proof?(client_proof, password_verifier, server_key_pair, client_public_key, options \\ []) View Source
valid_client_proof?(binary(), binary(), SRP.KeyPair.t(), binary(), Keyword.t()) ::
  boolean()

Validate the client’s proof of identity.

Examples

iex> identity = SRP.new_identity("bob", "password123")
iex> identity_verifier = SRP.generate_verifier(identity)
iex> client_key_pair = SRP.client_key_pair()
iex> server_key_pair = SRP.server_key_pair(identity_verifier.password_verifier)
iex> client_proof =
...>  SRP.client_proof(
...>    identity,
...>    identity_verifier.salt,
...>    client_key_pair,
...>    server_key_pair.public
...>  )
iex> SRP.valid_client_proof?(
...>   client_proof,
...>   identity_verifier.password_verifier,
...>   server_key_pair,
...>   client_key_pair.public
...> )
true
Link to this function valid_server_proof?(server_proof, identity, salt, client_key_pair, server_public_key, options \\ []) View Source
valid_server_proof?(
  binary(),
  SRP.Identity.t(),
  binary(),
  SRP.KeyPair.t(),
  binary(),
  Keyword.t()
) :: boolean()

Validate the server’s proof of identity.

Examples

iex> identity = SRP.new_identity("bob", "password123")
iex> identity_verifier = SRP.generate_verifier(identity)
iex> client_key_pair = SRP.client_key_pair()
iex> server_key_pair = SRP.server_key_pair(identity_verifier.password_verifier)
iex> client_proof =
...>   SRP.client_proof(
...>     identity,
...>     identity_verifier.salt,
...>     client_key_pair,
...>     server_key_pair.public
...>   )
iex> server_proof =
...>   SRP.server_proof(
...>     client_proof,
...>     identity_verifier.password_verifier,
...>     server_key_pair,
...>     client_key_pair.public
...>   )
iex> SRP.valid_server_proof?(
...>   server_proof,
...>   identity,
...>   identity_verifier.salt,
...>   client_key_pair,
...>   server_key_pair.public
...> )
true