easy-srp 0.2.0

easy-srp wraps the rust srp crate and provides an easy to use API.
Documentation
# Wrapper crate for the `srp` for easy usage

## Overview

The [srp](https://crates.io/crates/srp) crate provides the means for using
SRP6a athentication.
This crate wraps the `srp` crate to make the authentication workflows
more obvious easy to use.

This is done via the following means:
  * provide `Workflow` structs for the 3 typical workflows:
    * generation of initial registration data
    * client authentication
    * server authentication
  * name methods `step#` with increasing number `#`, so it is obvious in
    which phase of the authentication the method needs to be called.
  * include automatic random value generation (for ephemeral keys and salt)

## Features
  
  * Support for the web via WebAssembly. Therefore, this crate can be used
    with WASM-based web-UI frameworks like [yew]https://crates.io/crates/yew.
  * Easy to use.

## Available rust/cargo `features`

  * `client`: Include client classes (`ClientAuthenticationWorkflow`,
    `ClientRegistrationWorkflow`, ...).
  * `server`: Include server classes (`ServerAuthenticationWorkflow`, etc.)
  * `base64`: Include the [base64]https://crates.io/crates/base64 crate
    and provide `base64` serialization for various byte array parameters.
  * `serialization`: Include the [serde]https://crates.io/crates/serde crate
    and add `Serialize` and `Deserialize` to various structs. This also
    enables the `base64` feature.
  * `js`: This crate uses the [getrandom]https://crates.io/crates/getrandom
    crate for generating random numbers (salt, client/server ephemeral key).
    `getrandom` supports a variety of platforms, including web browsers (in
    WebAssembly). If `easy-srp` is used inside the browser, use the `js`
    feature to make `getrandom` use the `Crypto.getRandomValues()` JavaScript
    method for random number generation.

## Usage

### SRP6a protocol

#### User registration

  1) User enters `username` and `password`
  2) Client generates random `salt`
  3) Client computes `verifier` from the above credentials
  4) Client sends `username`, `salt` and `verifier` to the server via 
     a secure channel.
  5) Server stores these three credentials.

#### User authentication

| Client               |      | Server                      |
| -------------------- | ---- | --------------------------- |
| `(username, pub_a)`  |  ->  |                             |
|                      |  <-  | `(salt, pub_b)`             |
| `(proof_a)`          |  ->  |                             |
|                      |  <-  | `(proof_b)`                 |

  1) client generates ephemeral private key `a` and derices its public key `pub_a`
  2) client temporarily stores `a` for this authentication session.
  3) client sends `(username, pub_a)` to the server.
  4) server looks up `(salt, verifier)` for `username`.
  5) server temporarily stores `pub_a` for this authentication session.
  6) server computes an ephemeral private key `b` and derives its public
     key `pub_b`.
  7) server sends `(salt, pub_b)` to the client.
  8) client calculates proof `proof_a` and sends `(proof_a)` to the server.
  9) server verifies `proof_a`
  10) server computes its own proof `proof_b` and sends it to the client
  11) client verifies `proof_b`
  12) both sides are now able to calculate a common secret key. the size of
     the key depends on the used digest, e.g. 256 bits (32 bytes) for `SHA256`

### Generate client registration data

```rust
use sha2::Sha256;
use easy_srp::groups::G_4096;
use easy_srp::client::{ClientRegistrationWorkflow, GenerateVerifierParams};

let verifier_wf = ClientRegistrationWorkflow::<Sha256>::new(&G_4096);

let username = "lorem".to_string();
let password = "ipsum".to_string();

let verifier = verifier_wf.generate_verifier(GenerateVerifierParams {
  username: username.clone(),
  password: password.clone(),
  salt: None
}).expect("could not generate verifier.");
```

### Client example

In this example, the `step#_on_server(...)` methods are just placeholders
to show what data needs to be sent to the server and what data the server
needs to reply with.

```rust
use sha2::Sha256;
use easy_srp::groups::G_4096;
use easy_srp::client::{ClientAuthenticationWorkflow, ClientStep1Result,
  ClientStep3Result, ClientStep3Params}

let username = "someuser";
let password = "somepassword";

let client_wf = crate::client::ClientAuthenticationWorkflow::<Sha256>::new(&G_4096);
// compute ephemeral key of the client:
let step1_result: ClientStep1Result = client_wf.step1().expect("could not compute step1");
// send username and step1_result.client_public_a to the server and get salt and server_public_a
let (salt, server_public_b) = step2_on_server(username, step1_result.client_public_a);
let step3_result: ClientStep3Result<Sha256> = client_wf.step3(ClientStep3Params {
  client_a: step1_result.client_private_a.as_slice(),
  username: username.clone(),
  password: password.clone(),
  salt,
  server_public_b
}).expect("could not compute step3");
let server_proof = step4_on_server(step3_result.proof());
step3_result.verify_server(server_proof).expect("invalid server proof.");
let key = step3_result.key();
```

### Server example

In this example, the `step#_on_client(...)` methods are placeholders to show
what data needs to be sent to/received from the client.
The `retrieve_stored_credentials(username)` method is a placeholder to
demonstrate that `salt` and `stored_verifier` (the verifier saved in the
user registration process) need to be looked up given the username.

```rust
use sha2::Sha256;
use easy_srp::groups::G_4096;

// receive username and client ephemeral public key from client
let (username, client_public_a) = step1_on_client();
let (salt, stored_verifier) = retrieve_stored_credentials(username);
let server_wf = crate::server::ServerAuthenticationWorkflow::<Sha256>::new(test_group);
let step2_result: ServerStep2Result = server_wf.step2(ServerStep2Params {
  stored_verifier: verifier.verifier.as_slice()
}).expect("could not compute step2");
let step4_result: ServerStep4Result<Sha256> = server_wf.step4(ServerStep4Params {
  client_public_a: step1_result.client_public_a.as_slice(),
  server_private_b: step2_result.server_private_b.as_slice(),
  stored_verifier: verifier.verifier.as_slice(),
  client_proof: step3_result.proof()
}).expect("could not compute step4");
// verify the client proof before sending any encrypted data (including the
// server proof) to the client.
step4_result.verify_client(step3_result.proof())
  .expect("could not verify client.");
let key = step4_result.key();
```

## Dependencies

The following dependencies are included intentionally (aka directly):

 * [srp]https://crates.io/crates/srp
 * [digest]https://crates.io/crates/digest (also included transitively 
   via `srp`)
 * [getrandom]https://crates.io/crates/getrandom
 * [base64]https://crates.io/crates/base64 (feature `base64`)
 * [serde]https://crates.io/crates/serde (feature `serde`)

Additional dependencies may be included transitively.

## License

This project is licensed under the BSD-3-Clause license. See
[LICENSE.txt](LICENSE.txt) for the full license.