using Microsoft.VisualStudio.TestTools.UnitTesting;
using S22.Sasl.Mechanisms;
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace S22.Sasl.Test {
///
/// Contains unit tests for the SASL SCRAM-SHA-1 authentication mechanism.
///
[TestClass]
public class ScramSha1Test {
///
/// Verifies the syntax of the client-first-message sent by the client to
/// initiate authentication.
///
[TestMethod]
[TestCategory("Scram-Sha-1")]
public void VerifyClientFirstMessage() {
SaslMechanism m = new SaslScramSha1("Foo", "Bar");
string clientInitial = Encoding.UTF8.GetString(
m.GetResponse(new byte[0]));
// Verify the syntax of the client-first-message.
bool valid = Regex.IsMatch(clientInitial,
"^[nyp],(a=[^,]+)?,(m=[^,]+,)?n=[^,]+,(r=[^,]+)(,.*)?");
Assert.IsTrue(valid);
}
///
/// Sends the client an illegal nonce value and verifies the client
/// subsequently raises an exception.
///
[TestMethod]
[TestCategory("Scram-Sha-1")]
[ExpectedException(typeof(SaslException))]
public void TamperedNonce() {
SaslMechanism m = new SaslScramSha1("Foo", "Bar");
// Skip the initial client response.
m.GetResponse(new byte[0]);
// Hand the client a server-first-message containing a nonce which is
// missing the mandatory client-nonce part.
byte[] serverFirst = Encoding.UTF8.GetBytes("r=123456789,s=MTIzNDU2" +
"Nzg5,i=4096");
// This should raise an exception.
m.GetResponse(serverFirst);
}
///
/// Verifies the various parts of a sample authentication exchange
/// directly taken from RFC 5802 ("SCRAM Authentication Exchange", p. 8).
///
[TestMethod]
[TestCategory("Scram-Sha-1")]
public void VerifyAuthenticationExchange() {
string username = "user", password = "pencil",
cnonce = "fyko+d2lbbFgONRv9qkxdawL";
SaslMechanism s = new SaslScramSha1(username, password, cnonce);
string initialResponse = Encoding.UTF8.GetString(
s.GetResponse(new byte[0]));
// Verify the syntax of the client-first-message.
Match m = Regex.Match(initialResponse,
"^[nyp],(a=[^,]+)?,(m=[^,]+,)?n=([^,]+),r=([^,]+)(,.*)?");
Assert.IsTrue(m.Success);
Assert.AreEqual(username, m.Groups[3].ToString());
Assert.AreEqual(cnonce, m.Groups[4].ToString());
// Hand the client the server-first-message.
byte[] serverFirst = Encoding.UTF8.GetBytes(
"r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92," +
"i=4096");
string clientFinal = Encoding.UTF8.GetString(
s.GetResponse(serverFirst));
string expectedClientFinal = "c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfc" +
"NHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=";
Assert.AreEqual(expectedClientFinal, clientFinal);
// Hand the client the server-last-message.
byte[] serverLast = Encoding.UTF8.GetBytes("v=rmF9pqV8S7suAoZWja4dJ" +
"RkFsKQ=");
clientFinal = Encoding.UTF8.GetString(s.GetResponse(serverLast));
Assert.AreEqual(String.Empty, clientFinal);
}
///
/// Helper method for conveniently converting the specified string to
/// Base64 using a decoding of UTF-8.
///
/// The string to base64-encode.
/// A base64-encoded string.
string ToBase64(string s) {
return Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
}
///
/// Helper method for conveniently decoding the specified base64-encoded
/// string using a decoding of UTF-8.
///
/// The base64-encoded string to decode.
/// A string constructed from the base64-decoded sequence
/// of bytes.
string FromBase64(string s) {
return Encoding.UTF8.GetString(Convert.FromBase64String(s));
}
}
}