scram_2/
lib.rs

1//! # Salted Challenge Response Authentication Mechanism (SCRAM)
2//!
3//! This implementation currently provides a client and a server for the SCRAM-SHA-256 mechanism
4//! according to [RFC5802](https://tools.ietf.org/html/rfc5802) and
5//! [RFC7677](https://tools.ietf.org/html/rfc7677). It doesn't support channel-binding.
6//!
7//! # Usage
8//!
9//! ## Client
10//! A typical usage scenario is shown below. For a detailed explanation of the methods please
11//! consider their documentation. In productive code you should replace the unwrapping by proper
12//! error handling.
13//!
14//! At first the user and the password must be supplied using either of the methods
15//! [`ScramClient::new`](client::ScramClient::new) or
16//! [`ScramClient::with_rng`](client::ScramClient::with_rng). These methods return
17//! a SCRAM state you can use to compute the first client message.
18//!
19//! The server and the client exchange four messages using the SCRAM mechanism. There is a rust type
20//! for each one of them. Calling the methods
21//! [`client_first`](client::ScramClient::client_first),
22//! [`handle_server_first`](client::ServerFirst::handle_server_first),
23//! [`client_final`](client::ClientFinal::client_final) and
24//! [`handle_server_final`](client::ServerFinal::handle_server_final) on the
25//! different types advances the SCRAM handshake step by step. Computing client messages never fails
26//! but processing server messages can result in failure.
27//!
28//! ``` rust,no_run
29//! use scram_2::ScramClient;
30//!
31//! // This function represents your I/O implementation.
32//! # #[allow(unused_variables)]
33//! fn send_and_receive(message: &str) -> String {
34//!     unimplemented!()
35//! }
36//!
37//! // Create a SCRAM state from the credentials.
38//! let scram = ScramClient::new("user", "password", None);
39//!
40//! // Get the client message and reassign the SCRAM state.
41//! let (scram, client_first) = scram.client_first();
42//!
43//! // Send the client first message and receive the servers reply.
44//! let server_first = send_and_receive(&client_first);
45//!
46//! // Process the reply and again reassign the SCRAM state. You can add error handling to
47//! // abort the authentication attempt.
48//! let scram = scram.handle_server_first(&server_first).unwrap();
49//!
50//! // Get the client final message and reassign the SCRAM state.
51//! let (scram, client_final) = scram.client_final();
52//!
53//! // Send the client final message and receive the servers reply.
54//! let server_final = send_and_receive(&client_final);
55//!
56//! // Process the last message. Any error returned means that the authentication attempt
57//! // wasn't successful.
58//! let () = scram.handle_server_final(&server_final).unwrap();
59//! ```
60//!
61//! ## Server
62//!
63//! The server is created to respond to incoming challenges from a client. A typical usage pattern,
64//! with a default provider is shown below. In production, you would implement an
65//! [`AuthenticationProvider`] that could look up user credentials based on a username
66//!
67//! The server and the client exchange four messages using the SCRAM mechanism. There is a rust type
68//! for each one of them. Calling the methods
69//! [`handle_client_first`](server::ScramServer::handle_client_first),
70//! [`server_first`](server::ServerFirst::server_first),
71//! [`handle_client_final`](server::ClientFinal::handle_client_final) and
72//! [`server_final`](server::ServerFinal::server_final) on the different
73//! types advances the SCRAM handshake step by step. Computing server messages never fails (unless
74//! the source of randomness for the nonce fails), but processing client messages can result in
75//! failure.
76//!
77//! The final step will not return an error if authentication failed, but will return an
78//! [`AuthenticationStatus`] which you can use to determine
79//! if authentication was successful or not.
80//!
81//! ```rust,no_run
82//! use scram_2::{ScramServer, AuthenticationStatus, AuthenticationProvider, PasswordInfo};
83//!
84//! // Create a dummy authentication provider
85//! struct ExampleProvider;
86//! impl AuthenticationProvider for ExampleProvider {
87//!     // Here you would look up password information for the the given username
88//!     fn get_password_for(&self, username: &str) -> Option<PasswordInfo> {
89//!        unimplemented!()
90//!     }
91//!
92//! }
93//! // These functions represent your I/O implementation.
94//! # #[allow(unused_variables)]
95//! fn receive() -> String {
96//!     unimplemented!()
97//! }
98//! # #[allow(unused_variables)]
99//! fn send(message: &str) {
100//!     unimplemented!()
101//! }
102//!
103//! // Create a new ScramServer using the example authenication provider
104//! let scram_server = ScramServer::new(ExampleProvider{});
105//!
106//! // Receive a message from the client
107//! let client_first = receive();
108//!
109//! // Create a SCRAM state from the client's first message
110//! let scram_server = scram_server.handle_client_first(&client_first).unwrap();
111//! // Craft a response to the client's message and advance the SCRAM state
112//! // We could use our own source of randomness here, with `server_first_with_rng()`
113//! let (scram_server, server_first) = scram_server.server_first();
114//! // Send our message to the client and read the response
115//! send(&server_first);
116//! let client_final = receive();
117//!
118//! // Process the client's challenge and re-assign the SCRAM state.  This could fail if the
119//! // message was poorly formatted
120//! let scram_server = scram_server.handle_client_final(&client_final).unwrap();
121//!
122//! // Prepare the final message and get the authentication status
123//! let(status, server_final) = scram_server.server_final();
124//! // Send our final message to the client
125//! send(&server_final);
126//!
127//! // Check if the client successfully authenticated
128//! assert_eq!(status, AuthenticationStatus::Authenticated);
129//! ```
130extern crate base64;
131extern crate rand;
132extern crate ring;
133
134/// The length of the client nonce in characters/bytes.
135const NONCE_LENGTH: usize = 24;
136
137#[macro_use]
138mod utils;
139pub mod client;
140mod error;
141pub mod server;
142
143pub use client::ScramClient;
144pub use error::{Error, Field, Kind};
145pub use server::{AuthenticationProvider, AuthenticationStatus, PasswordInfo, ScramServer};
146pub use utils::hash_password;