Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Salted Challenge Response Authentication Mechanism (SCRAM) SHA-1

4.94/5 (20 votes)
26 Mar 2014CPOL9 min read 54.9K   1.2K  
A C# implementation of the SCRAM SHA-1 (RFC 5802) authentication protocol.

Introduction

Two fundamental qualities necessary for security assurances over a telecommunication channel are confidentiality and authentication. Confidentiality assures that only the right parties have access to the communication stream, and authentication attempts to guarantee that the parties at each end of the communication channel are who they say they are. The Salted Challenge Response Authentication Mechanism (SCRAM) SHA-1 is a standardized authentication technique defined in RFC 5802. This article discusses this mechanism briefly using the codes attached.

The primary motivation for this work was that I was unable to find a full C# implementation of the SCRAM-SHA1 protocol for use on the project I am currently working on and so I decided to implement it myself. Hopefully, you wouldn’t have to go through the minor hassle of writing a C# implementation if you ever needed it.

You are welcome :D

Background

The primary means to authenticate oneself to an entity is to show that one holds some restricted or secret information that can be presented when challenged. Typically, within the context of Information and Communication Technology (ICT), one presents a user name and/or a password that authenticates one to the challenging entity. Sometimes the authentication process has to be mutual i.e., party A authenticates herself to party B and party B authenticates himself to party A.

SCRAM SHA-1 belongs to a class of protocols that provide the mutual authentication facility. Another similar protocol is the Socialist Millionaire Protocol (SMP) discussed here . In SCRAM SHA-1, as with most secure authentication mechanisms, the password or secret information is never transmitted during the execution of the protocol. Instead, the secret information in addition to a cryptographically secure random value is used to compute another value at each end of the communication channel. It is this computed value that is transmitted. On receipt of the transmitted value the communicating parties compare their computed value to the received value. If these values match then the authentication process is successful, otherwise it fails.

The SCRAM SHA-1 Mechanism

The SCRAM SHA-1 protocol assumes that one end of the communication channel is the client and the other end is a server. Keep this in mind as you read the rest of the article.

The SCRAM SHA-1 Message Sequence

SCRAM SHA-1 Message Sequence
Message Sequence

The execution of SCRAM SHA-1 involves the transmission and processing of four messages; two each by/for the client and server. As can be seen in the image above, the client begins the process by sending the Client First Message and in response to the reception of a validly formatted first message from the client, the server sends the Server First Message to the client. The client processes this message and if everything is okay, it transmits the Client Final Message. The server, as expected, processes this message. At the end of this task, the server should know if the client is successfully authenticated or not. If it is, the server sends the Server Final Message, otherwise it sends an authentication failure message to the client (or maybe not). With the reception of the Server Final Message, the client is also able to authenticate the server.

The SCRAM SHA-1 Cryptographic Values

There are four cryptographic values that are central to the SCRAM SHA-1 protocol. These values are;

  • Client nonce: This is a value that is randomly generated by the client, ideally using a cryptographic random generator. This value, along with the client’s user name, is contained in the Client First Message. Note that the client nonce value must be different for each authentication session.

  • Server nonce: This is similar to the client nonce and it is contained in the Server First Message. This value must be different for each authentication session and be cryptographically secure.

  • Salt: The Salt is a cryptographically secure random number generated by the server. This Salt value and the password are fed into a one-way cryptographic function that generates another value. Recall from the background section that this value was used to hide the password. The Salt is contained in the Server First Message.

  • Iteration Count: This is a numerical value generated by the server that indicates how many times the cryptographic function mentioned above should be applied to the Salt and the password to generate its output. This Iteration Count value is transmitted in the Server First Message.

Caveat

The SCRAM SHA-1 specification strongly advises that the protocol should be used in conjunction with another protocol that provides confidentiality. In other words, the SCRAM SHA-1 messages should be exchanged over an encrypted channel. The idea is to prevent an eavesdropper from extracting the contents of these messages in transit and then using the values contained within to mount an off-line dictionary attack to extract the password.

For the demo presented here, the Transport Layer Security (TLS) is used to provide confidentiality for the authentication protocol.

Using the code

The routines associated with the implemented SCRAM SHA-1 protocol are presented in this section. Also presented is the output for the authentication protocol using the test values (or vectors) given in the specification. This section ends by showing the outputs of both the client and the server when the implemented protocol runs over TLS.

The implemented SCRAM SHA-1 routines

C#
// Client routines 


string _user_name = "user";
string _password = "pencil"; 
string _message = string.Empty;
bool _is_server_authenticated = false;

// Initialize the protocol object
ScramSha1 _client = new ScramSha1(USER_MODE.CLIENT, _user_name, _password);

// Get the client’s first message
_message = _client.GetClientFirstMessage();

// The client sends the message to the server and waits for the server first message 
// Assume that the server's first message has been received and is contained in _server_first_message

// Get the client’s final message
_message = _client.GetClientFinalMessage(_server_first_message);

// The client sends this final message to the server and waits for the server's final message 
// Assume that the server first message has been received and is contained in _server_final_message

_is_server_authenticated = _client.VerifyServerSignature(_server_final_message);

if (_is_server_authenticated == true)
 Console.WriteLine("The server is authenticated \n");
 else 
{ 
 Console.WriteLine("The server is not authenticated \n");
 // Send authentication failure message if it is the policy 
}

This protocol implementation can be run in one of two modes: Server or Client. To begin the authentication process as a client, the class constructor i.e.,

ScramSha1(USER_MODE, string,
string)
, is called. The constructor is passed the parameters: user mode, user name and password as shown in the code extract above. The Client First Message is retrieved by calling the GetClientFirstMessage() routine. This message is sent to the server. As soon as the Server First Message is received, the client passes it to the GetClientFinalMessage(string) routine. This routine returns the Client Final Message which is transmitted to the server. Finally, the received Server Final Message is passed to the VerifyServerSignature(string) routine. The bool value returned by this routine indicates if the server has been authenticated or not.

The implementation sets the default length of the client nonce to 32 bytes. In order to increase the difficulty for an off-line dictionary attack, the client might want to increase the length of this nonce. This can be done by instantiating the class in client mode using the constructor ScramSha1(USER_MODE, string, string, int), and passing it the desired length of the client nonce.

If an error were to occur during the processing of the received server messages then the GetClientFirstMessage() and GetClientFinalMessage(string) routines return an empty string. These errors could be as result of an improperly formatted server message.

C#
// Server routines 


string _user_name = string.empty; 
string _password = string.empty; 
string _message = string.Empty;
bool _is_client_authenticated = false;

// Assume that the server has just received the first message of the client and it is contained on _client_first_message 
// Initialize the protocol object
ScramSha1 _server = new ScramSha1(USER_MODE.SERVER, _client_first_message);
 
 // Get the user name 
_user_name = _server.GetUserName();
 // Use the _user_name to get the associated password from the authentication database and set this value in _password.

// Get the server’s first message
_message = _server.GetServerFirstMessage(_password);

// The server sends the message to the client and waits for the client's final message 
// Assume that the client's final message has been received and is contained in _client_final_message

// Get the server’s final message
_message = _server.GetServerFinalMessage(_client_final_message, ref _is_client_authenticated);

if (_is_client_authenticated == true)
{ 
 Console.WriteLine("The client is authenticated \n"); 
 //Send the server final message 
}
 else 
{
 Console.WriteLine("The client is not authenticated \n"); 
 // Send authentication failure message if it is the policy 
}

Executing the protocol in server mode follows the pattern shown in the code extract above. Note that the constructor used in this case differs from those used in the client mode. The server mode can only be instantiated when the Client First Message has been received. The received client message is passed to this constructor: ScramSha1(USER_MODE, string). The server can get the user name of the client by calling the GetUserName() routine. The server uses this user name to get the password from the authentication database. If it exists, the password is passed to the GetServerFirstMessage(string) routine. This routine returns a string containing the Server First Message which is transmitted to the client. When the server receives the Client Final Message, it passes it to the GetServerFinalMessage(string ,ref bool) routine. This routine is also passed (by reference) a bool variable. If the bool variable value is true, then the client is authenticated and the string returned by this routine is sent to the client as can be seen in the code extract above. Otherwise, an authentication failure message can be sent instead.

The length of the server nonce as well as that of the Salt can be set by calling the

ScramSha1(USER_MODE, string, int,
int)
constructor in server mode. The reason for increasing the client nonce also applies to the server nonce and Salt.

The GetServerFirstMessage(string) routine returns an empty string when an error occurs during the processing of the received client message. The

GetServerFinalMessage(string ,ref
bool)
routine, on the other hand, returns an empty string when an error occurs or when the client is not authenticated.

In server mode, the lower and upper limits of the iteration count can be set by calling the

SetIterationCountLimits(int,
int)
routine. The default lower and upper limits are 4000 and 5000, respectively. It is important that these limits are not set too high or too low. Setting it too low improves the chances of a successful off-line dictionary attack on this authentication mechanism. If it is set too high, it might overwhelm a client running on a limited processor device. In order to prevent a malicious server from degrading the performance of a client device, the client can set the maximum allowable number of iterations it can accommodate by passing this value to the SetMaximumIterationCount(int) routine. The default value for this client maximum is 10000. If the iteration count received from the server is above this maximum, the authentication process fails.

Executing SCRAM SHA-1 with Test Vectors

The SCRAM SHA-1 protocol RFC provides values that are used to test the conformity of an implementation to the authentication specification. Three things are required to run this implementation using the test vectors: the UseTestVectors(bool) static routine of the class is called and passed a parameter true to set the instance to test mode. Secondly, the user name is set to “user”, and lastly, the password is set to “pencil”. To see the output of the authentication protocol in test mode, run an instance of the attached console application, type the letter “T” and press enter. The screen should give an output similar to that of the image below .

SCRAM SHA-1 Test Vector
SCRAM SHA-1 test vector output

Executing SCRAM SHA-1 over TLS

The TLS implementation used in this work was deliberately made inelegant in order to keep it simple enough to demonstrate the use of the authentication protocol over TLS.

The TLS implementation of the server is based on the SuperSocket class of Kerry Jiang. The server's SCRAM SHA-1 logic can be found in the ScramCommand class. That of the client is contained in the SCRAMSha1TestClient class.

To begin, an instance of the application should be run. Type the letter “S” and press enter to start it in server mode. Then run another instance and type the letter “C” and press enter to start it in client mode. If everything goes according to plan you should see the screen shots shown below.

SCRAM SHA-1 ouput client
SCRAM SHA-1 output from client perspective
SCRAM SHA-1 ouput client
SCRAM SHA-1 output from server perspective

To force the authentication process to fail you can set one password for the client and another for the server and observe the outputs.

History

17/12/2013: First version

26/03/2014: Made the client and the server TLS mechanisms more robust.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)