UtilPack.Cryptography.SASL.SCRAM 2.0.0

Provides ability to create objects implementing SASLMechanism of UtilPack.Cryptograhy.SASL that handle the SCRAM mechanism of SASL. No strings are allocated during challenge. Both client-side and server-side mechanisms are provided.

Install-Package UtilPack.Cryptography.SASL.SCRAM -Version 2.0.0
dotnet add package UtilPack.Cryptography.SASL.SCRAM --version 2.0.0
<PackageReference Include="UtilPack.Cryptography.SASL.SCRAM" Version="2.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add UtilPack.Cryptography.SASL.SCRAM --version 2.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

UtilPack.Cryptography.SASL.SCRAM

This is library implementing SCRAM-(SHA128|256|512) protocol without dynamically allocating any strings.
The SCRAM protocol handlers are accessible via extension methods for BlockDigestAlgorithm interface of UtilPack.Cryptography.Digest project.

Here is an example for authenticating as a client:

using UtilPack.Cryptography.Digest;

// Example of using SCRAM-SHA256
// Variables username, password, and stream are assumed to be coming from elsewhere in this example.
using ( var client = new SHA256().CreateSASLClientSCRAM() )
{
  var encoding = new UTF8Encoding( false, false ).CreateDefaultEncodingInfo();
  var writeArray = new ResizableArray<Byte>();
  var credentials = new SASLCredentialsSCRAMForClient(
    username,
    password // password may be clear-text password as string, or result of PBKDF2 iteration as byte array.
    );

  // Create client-first message
  (var bytesWritten, var challengeResult) = await client.ChallengeOrThrowOnErrorAsync( credentials.CreateChallengeArguments(
    null, // Initial phase does not read anything
    -1,
    -1,
    writeArray,
    0,
    encoding
    ) );

  // Write client-first message
  await stream.WriteAsync( writeArray.Array, 0, bytesWritten );

  // Read server-first message
  var readBytes = new Byte[10000]; // Assume static max size for this small example
  var readCount = await stream.ReadAsync( readBytes, 0, readBytes.Length ); // Assume this simple and na�ve read for this small example

  // Create client-final message
  (bytesWritten, challengeResult) = await client.ChallengeOrThrowOnErrorAsync( credentials.CreateChallengeArguments(
    readBytes,
    0,
    readCount,
    writeArray,
    0,
    encoding
    ) );

  // At this point, credentials.PasswordDigest will contain result of PBKDF2 iteration, if cleartext password as specified earlier

  // Write client-final message
  await stream.WriteAsync( writeArray.Array, 0, bytesWritten );

  // Read server-final message
  var readCount = await stream.ReadAsync(readBytes, 0, readBytes.Length );
  
  // Validate server-final message
  (bytesWritten, challengeResult) = await client.ChallengeOrThrowOnErrorAsync( credentials.CreateChallengeArguments(
    readBytes,
    0,
    readCount,
    writeArray,
    0,
    encoding
    ) );

  // Now bytesWritten will be 0, and challengeResult will be SASLChallengeResult.Completed.
  // An exception will be thrown on authentication error, or if server sents wrong messaage.
}

Distribution

See NuGet package for binary distribution.

TODO

Modify code as needed after starting to use Span<T> (currently, the code for client and server SCRAM not the prettiest code there is).
This will require a polyfill (in UtilPack, most likely) for .NET 4.0.

UtilPack.Cryptography.SASL.SCRAM

This is library implementing SCRAM-(SHA128|256|512) protocol without dynamically allocating any strings.
The SCRAM protocol handlers are accessible via extension methods for BlockDigestAlgorithm interface of UtilPack.Cryptography.Digest project.

Here is an example for authenticating as a client:

using UtilPack.Cryptography.Digest;

// Example of using SCRAM-SHA256
// Variables username, password, and stream are assumed to be coming from elsewhere in this example.
using ( var client = new SHA256().CreateSASLClientSCRAM() )
{
  var encoding = new UTF8Encoding( false, false ).CreateDefaultEncodingInfo();
  var writeArray = new ResizableArray<Byte>();
  var credentials = new SASLCredentialsSCRAMForClient(
    username,
    password // password may be clear-text password as string, or result of PBKDF2 iteration as byte array.
    );

  // Create client-first message
  (var bytesWritten, var challengeResult) = await client.ChallengeOrThrowOnErrorAsync( credentials.CreateChallengeArguments(
    null, // Initial phase does not read anything
    -1,
    -1,
    writeArray,
    0,
    encoding
    ) );

  // Write client-first message
  await stream.WriteAsync( writeArray.Array, 0, bytesWritten );

  // Read server-first message
  var readBytes = new Byte[10000]; // Assume static max size for this small example
  var readCount = await stream.ReadAsync( readBytes, 0, readBytes.Length ); // Assume this simple and na�ve read for this small example

  // Create client-final message
  (bytesWritten, challengeResult) = await client.ChallengeOrThrowOnErrorAsync( credentials.CreateChallengeArguments(
    readBytes,
    0,
    readCount,
    writeArray,
    0,
    encoding
    ) );

  // At this point, credentials.PasswordDigest will contain result of PBKDF2 iteration, if cleartext password as specified earlier

  // Write client-final message
  await stream.WriteAsync( writeArray.Array, 0, bytesWritten );

  // Read server-final message
  var readCount = await stream.ReadAsync(readBytes, 0, readBytes.Length );
  
  // Validate server-final message
  (bytesWritten, challengeResult) = await client.ChallengeOrThrowOnErrorAsync( credentials.CreateChallengeArguments(
    readBytes,
    0,
    readCount,
    writeArray,
    0,
    encoding
    ) );

  // Now bytesWritten will be 0, and challengeResult will be SASLChallengeResult.Completed.
  // An exception will be thrown on authentication error, or if server sents wrong messaage.
}

Distribution

See NuGet package for binary distribution.

TODO

Modify code as needed after starting to use Span<T> (currently, the code for client and server SCRAM not the prettiest code there is).
This will require a polyfill (in UtilPack, most likely) for .NET 4.0.

Release Notes

Updating to newer version of UtilPack.Cryptography.Digest. This introduces binary-incompatible change, resulting in major version number increase.

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
2.0.0 289 5/26/2018
1.0.0-RC1 238 10/29/2017